diff --git a/.azure/pipelines/ci.yml b/.azure/pipelines/ci.yml
index 9b491337a970..6c4e6adbcde6 100644
--- a/.azure/pipelines/ci.yml
+++ b/.azure/pipelines/ci.yml
@@ -674,9 +674,9 @@ stages:
# Build the shared framework
- script: ./build.cmd -ci -all -pack -arch x64 -buildNative /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log /bl:artifacts/log/helix.build.x64.binlog
displayName: Build shared fx
- - script: .\restore.cmd -ci
+ - script: .\restore.cmd -ci /p:BuildInteropProjects=true
displayName: Restore
- - script: .\build.cmd -ci -NoRestore -test -projects eng\helix\helix.proj /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildAllProjects=true /p:BuildNative=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log -bl
+ - script: .\build.cmd -ci -NoRestore -test -projects eng\helix\helix.proj /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildAllProjects=true /p:BuildInteropProjects=true /p:BuildNative=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log -bl
displayName: Run build.cmd helix target
env:
HelixApiAccessToken: $(HelixApiAccessToken) # Needed for internal queues
@@ -701,7 +701,7 @@ stages:
# Build the x86 shared framework
- script: .\restore.cmd -ci
displayName: Restore
- - script: .\build.cmd -ci -NoRestore -test -projects eng\helix\helix.proj /p:IsHelixJob=true /p:IsHelixDaily=true /p:BuildAllProjects=true /p:BuildNative=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log -bl
+ - script: .\build.cmd -ci -NoRestore -test -projects eng\helix\helix.proj /p:IsHelixJob=true /p:IsHelixDaily=true /p:BuildAllProjects=true /p:BuildInteropProjects=true /p:BuildNative=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log -bl
displayName: Run build.cmd helix target
env:
HelixApiAccessToken: $(HelixApiAccessToken) # Needed for internal queues
diff --git a/.azure/pipelines/quarantined-tests.yml b/.azure/pipelines/quarantined-tests.yml
index 415853ed8e18..791283efd6f9 100644
--- a/.azure/pipelines/quarantined-tests.yml
+++ b/.azure/pipelines/quarantined-tests.yml
@@ -35,7 +35,7 @@ jobs:
displayName: Build shared fx
- script: .\restore.cmd -ci
displayName: Restore
- - script: .\build.cmd -ci -NoRestore -test -noBuildJava -projects eng\helix\helix.proj /p:RunQuarantinedTests=true /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildAllProjects=true /p:BuildNative=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log -bl
+ - script: .\build.cmd -ci -NoRestore -test -noBuildJava -projects eng\helix\helix.proj /p:RunQuarantinedTests=true /p:IsRequiredCheck=true /p:IsHelixJob=true /p:BuildAllProjects=true /p:BuildInteropProjects=true /p:BuildNative=true /p:RunTemplateTests=true /p:ASPNETCORE_TEST_LOG_DIR=artifacts/log -bl
displayName: Run build.cmd helix target
env:
HelixApiAccessToken: $(HelixApiAccessToken) # Needed for internal queues
diff --git a/Directory.Build.targets b/Directory.Build.targets
index a62e3ed6a48b..9a30afbb8e09 100644
--- a/Directory.Build.targets
+++ b/Directory.Build.targets
@@ -167,5 +167,7 @@
+
+
diff --git a/eng/Build.props b/eng/Build.props
index 4c0542db15da..6f1b3f190898 100644
--- a/eng/Build.props
+++ b/eng/Build.props
@@ -123,6 +123,13 @@
+
+
+
+
+
+
+
+
+
+ $(RestoreAdditionalProjectSources);$(ArtifactsShippingPackagesDir)
+ $(TargetingPackVersion)
+ $(AspNetCoreBaselineVersion)
+
+
+
+
+
+
+
diff --git a/src/Grpc/test/testassets/InteropWebsite/InteropWebsite.csproj b/src/Grpc/test/testassets/InteropWebsite/InteropWebsite.csproj
new file mode 100644
index 000000000000..4b8d6d38509f
--- /dev/null
+++ b/src/Grpc/test/testassets/InteropWebsite/InteropWebsite.csproj
@@ -0,0 +1,19 @@
+
+
+
+ $(DefaultNetCoreTargetFramework)
+ InProcess
+ false
+ enable
+ 8.0
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Grpc/test/testassets/InteropWebsite/Program.cs b/src/Grpc/test/testassets/InteropWebsite/Program.cs
new file mode 100644
index 000000000000..fffaf75c559e
--- /dev/null
+++ b/src/Grpc/test/testassets/InteropWebsite/Program.cs
@@ -0,0 +1,63 @@
+#region Copyright notice and license
+
+// Copyright 2019 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.IO;
+using Microsoft.AspNetCore.Hosting;
+using Microsoft.AspNetCore.Server.Kestrel.Core;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.Hosting;
+
+namespace InteropTestsWebsite
+{
+ public class Program
+ {
+ public static void Main(string[] args)
+ {
+ CreateHostBuilder(args).Build().Run();
+ }
+
+ public static IHostBuilder CreateHostBuilder(string[] args) =>
+ Host.CreateDefaultBuilder(args)
+ .ConfigureWebHostDefaults(webBuilder =>
+ {
+ webBuilder.ConfigureKestrel((context, options) =>
+ {
+ // Support --port and --use_tls cmdline arguments normally supported
+ // by gRPC interop servers.
+ var useTls = context.Configuration.GetValue("use_tls", false);
+
+ options.Limits.MinRequestBodyDataRate = null;
+ options.ListenAnyIP(0, listenOptions =>
+ {
+ Console.WriteLine($"Enabling connection encryption: {useTls}");
+
+ if (useTls)
+ {
+ var basePath = Path.GetDirectoryName(typeof(Program).Assembly.Location);
+ var certPath = Path.Combine(basePath!, "Certs", "server1.pfx");
+
+ listenOptions.UseHttps(certPath, "1111");
+ }
+ listenOptions.Protocols = HttpProtocols.Http2;
+ });
+ });
+ webBuilder.UseStartup();
+ });
+ }
+}
diff --git a/src/Grpc/test/testassets/InteropWebsite/Startup.cs b/src/Grpc/test/testassets/InteropWebsite/Startup.cs
new file mode 100644
index 000000000000..4715d1fbbca4
--- /dev/null
+++ b/src/Grpc/test/testassets/InteropWebsite/Startup.cs
@@ -0,0 +1,49 @@
+#region Copyright notice and license
+
+// Copyright 2019 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 Grpc.Testing;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Hosting;
+
+namespace InteropTestsWebsite
+{
+ public class Startup
+ {
+ // This method gets called by the runtime. Use this method to add services to the container.
+ // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddGrpc();
+ }
+
+ // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
+ public void Configure(IApplicationBuilder app, IHostApplicationLifetime applicationLifetime)
+ {
+ // Required to notify test infrastructure that it can begin tests
+ applicationLifetime.ApplicationStarted.Register(() => Console.WriteLine("Application started."));
+
+ app.UseRouting();
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapGrpcService();
+ });
+ }
+ }
+}
diff --git a/src/Grpc/test/testassets/InteropWebsite/TestServiceImpl.cs b/src/Grpc/test/testassets/InteropWebsite/TestServiceImpl.cs
new file mode 100644
index 000000000000..918500cd2562
--- /dev/null
+++ b/src/Grpc/test/testassets/InteropWebsite/TestServiceImpl.cs
@@ -0,0 +1,149 @@
+#region Copyright notice and license
+
+// Copyright 2015-2016 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.Linq;
+using System.Threading.Tasks;
+using Google.Protobuf;
+using Grpc.Core;
+using InteropTestsWebsite;
+
+namespace Grpc.Testing
+{
+ // Implementation copied from https://github.com/grpc/grpc/blob/master/src/csharp/Grpc.IntegrationTesting/TestServiceImpl.cs
+ public class TestServiceImpl : TestService.TestServiceBase
+ {
+ public override Task EmptyCall(Empty request, ServerCallContext context)
+ {
+ return Task.FromResult(new Empty());
+ }
+
+ public override async Task UnaryCall(SimpleRequest request, ServerCallContext context)
+ {
+ await EnsureEchoMetadataAsync(context, request.ResponseCompressed?.Value ?? false);
+ EnsureEchoStatus(request.ResponseStatus, context);
+ EnsureCompression(request.ExpectCompressed, context);
+
+ var response = new SimpleResponse { Payload = CreateZerosPayload(request.ResponseSize) };
+ return response;
+ }
+
+ public override async Task StreamingOutputCall(StreamingOutputCallRequest request, IServerStreamWriter responseStream, ServerCallContext context)
+ {
+ await EnsureEchoMetadataAsync(context, request.ResponseParameters.Any(rp => rp.Compressed?.Value ?? false));
+ EnsureEchoStatus(request.ResponseStatus, context);
+
+ foreach (var responseParam in request.ResponseParameters)
+ {
+ responseStream.WriteOptions = !(responseParam.Compressed?.Value ?? false)
+ ? new WriteOptions(WriteFlags.NoCompress)
+ : null;
+
+ var response = new StreamingOutputCallResponse { Payload = CreateZerosPayload(responseParam.Size) };
+ await responseStream.WriteAsync(response);
+ }
+ }
+
+ public override async Task StreamingInputCall(IAsyncStreamReader requestStream, ServerCallContext context)
+ {
+ await EnsureEchoMetadataAsync(context);
+
+ int sum = 0;
+ await requestStream.ForEachAsync(request =>
+ {
+ EnsureCompression(request.ExpectCompressed, context);
+
+ sum += request.Payload.Body.Length;
+ return Task.CompletedTask;
+ });
+ return new StreamingInputCallResponse { AggregatedPayloadSize = sum };
+ }
+
+ public override async Task FullDuplexCall(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context)
+ {
+ await EnsureEchoMetadataAsync(context);
+
+ await requestStream.ForEachAsync(async request =>
+ {
+ EnsureEchoStatus(request.ResponseStatus, context);
+ foreach (var responseParam in request.ResponseParameters)
+ {
+ var response = new StreamingOutputCallResponse { Payload = CreateZerosPayload(responseParam.Size) };
+ await responseStream.WriteAsync(response);
+ }
+ });
+ }
+
+ public override Task HalfDuplexCall(IAsyncStreamReader requestStream, IServerStreamWriter responseStream, ServerCallContext context)
+ {
+ throw new NotImplementedException();
+ }
+
+ private static Payload CreateZerosPayload(int size)
+ {
+ return new Payload { Body = ByteString.CopyFrom(new byte[size]) };
+ }
+
+ private static async Task EnsureEchoMetadataAsync(ServerCallContext context, bool enableCompression = false)
+ {
+ var echoInitialList = context.RequestHeaders.Where((entry) => entry.Key == "x-grpc-test-echo-initial").ToList();
+
+ // Append grpc internal compression header if compression is requested by the client
+ if (enableCompression)
+ {
+ echoInitialList.Add(new Metadata.Entry("grpc-internal-encoding-request", "gzip"));
+ }
+
+ if (echoInitialList.Any()) {
+ var entry = echoInitialList.Single();
+ await context.WriteResponseHeadersAsync(new Metadata { entry });
+ }
+
+ var echoTrailingList = context.RequestHeaders.Where((entry) => entry.Key == "x-grpc-test-echo-trailing-bin").ToList();
+ if (echoTrailingList.Any()) {
+ context.ResponseTrailers.Add(echoTrailingList.Single());
+ }
+ }
+
+ private static void EnsureEchoStatus(EchoStatus responseStatus, ServerCallContext context)
+ {
+ if (responseStatus != null)
+ {
+ var statusCode = (StatusCode)responseStatus.Code;
+ context.Status = new Status(statusCode, responseStatus.Message);
+ }
+ }
+
+ private static void EnsureCompression(BoolValue? expectCompressed, ServerCallContext context)
+ {
+ if (expectCompressed != null)
+ {
+ // ServerCallContext.RequestHeaders filters out grpc-* headers
+ // Get grpc-encoding from HttpContext instead
+ var encoding = context.GetHttpContext().Request.Headers.SingleOrDefault(h => h.Key == "grpc-encoding").Value.SingleOrDefault();
+ if (expectCompressed.Value)
+ {
+ if (encoding == null || encoding == "identity")
+ {
+ throw new RpcException(new Status(StatusCode.InvalidArgument, string.Empty));
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/src/Grpc/test/testassets/Proto/grpc/testing/empty.proto b/src/Grpc/test/testassets/Proto/grpc/testing/empty.proto
new file mode 100644
index 000000000000..6a0aa88dfde1
--- /dev/null
+++ b/src/Grpc/test/testassets/Proto/grpc/testing/empty.proto
@@ -0,0 +1,28 @@
+
+// Copyright 2015 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.testing;
+
+// An empty message that you can re-use to avoid defining duplicated empty
+// messages in your project. A typical example is to use it as argument or the
+// return value of a service API. For instance:
+//
+// service Foo {
+// rpc Bar (grpc.testing.Empty) returns (grpc.testing.Empty) { };
+// };
+//
+message Empty {}
diff --git a/src/Grpc/test/testassets/Proto/grpc/testing/messages.proto b/src/Grpc/test/testassets/Proto/grpc/testing/messages.proto
new file mode 100644
index 000000000000..7b1b7286dced
--- /dev/null
+++ b/src/Grpc/test/testassets/Proto/grpc/testing/messages.proto
@@ -0,0 +1,165 @@
+
+// Copyright 2015-2016 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.
+
+// Message definitions to be used by integration test service definitions.
+
+syntax = "proto3";
+
+package grpc.testing;
+
+// TODO(dgq): Go back to using well-known types once
+// https://github.com/grpc/grpc/issues/6980 has been fixed.
+// import "google/protobuf/wrappers.proto";
+message BoolValue {
+ // The bool value.
+ bool value = 1;
+}
+
+// The type of payload that should be returned.
+enum PayloadType {
+ // Compressable text format.
+ COMPRESSABLE = 0;
+}
+
+// A block of data, to simply increase gRPC message size.
+message Payload {
+ // The type of data in body.
+ PayloadType type = 1;
+ // Primary contents of payload.
+ bytes body = 2;
+}
+
+// A protobuf representation for grpc status. This is used by test
+// clients to specify a status that the server should attempt to return.
+message EchoStatus {
+ int32 code = 1;
+ string message = 2;
+}
+
+// Unary request.
+message SimpleRequest {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, server randomly chooses one from other formats.
+ PayloadType response_type = 1;
+
+ // Desired payload size in the response from the server.
+ int32 response_size = 2;
+
+ // Optional input payload sent along with the request.
+ Payload payload = 3;
+
+ // Whether SimpleResponse should include username.
+ bool fill_username = 4;
+
+ // Whether SimpleResponse should include OAuth scope.
+ bool fill_oauth_scope = 5;
+
+ // Whether to request the server to compress the response. This field is
+ // "nullable" in order to interoperate seamlessly with clients not able to
+ // implement the full compression tests by introspecting the call to verify
+ // the response's compression status.
+ BoolValue response_compressed = 6;
+
+ // Whether server should return a given status
+ EchoStatus response_status = 7;
+
+ // Whether the server should expect this request to be compressed.
+ BoolValue expect_compressed = 8;
+}
+
+// Unary response, as configured by the request.
+message SimpleResponse {
+ // Payload to increase message size.
+ Payload payload = 1;
+ // The user the request came from, for verifying authentication was
+ // successful when the client expected it.
+ string username = 2;
+ // OAuth scope.
+ string oauth_scope = 3;
+}
+
+// Client-streaming request.
+message StreamingInputCallRequest {
+ // Optional input payload sent along with the request.
+ Payload payload = 1;
+
+ // Whether the server should expect this request to be compressed. This field
+ // is "nullable" in order to interoperate seamlessly with servers not able to
+ // implement the full compression tests by introspecting the call to verify
+ // the request's compression status.
+ BoolValue expect_compressed = 2;
+
+ // Not expecting any payload from the response.
+}
+
+// Client-streaming response.
+message StreamingInputCallResponse {
+ // Aggregated size of payloads received from the client.
+ int32 aggregated_payload_size = 1;
+}
+
+// Configuration for a particular response.
+message ResponseParameters {
+ // Desired payload sizes in responses from the server.
+ int32 size = 1;
+
+ // Desired interval between consecutive responses in the response stream in
+ // microseconds.
+ int32 interval_us = 2;
+
+ // Whether to request the server to compress the response. This field is
+ // "nullable" in order to interoperate seamlessly with clients not able to
+ // implement the full compression tests by introspecting the call to verify
+ // the response's compression status.
+ BoolValue compressed = 3;
+}
+
+// Server-streaming request.
+message StreamingOutputCallRequest {
+ // Desired payload type in the response from the server.
+ // If response_type is RANDOM, the payload from each response in the stream
+ // might be of different types. This is to simulate a mixed type of payload
+ // stream.
+ PayloadType response_type = 1;
+
+ // Configuration for each expected response message.
+ repeated ResponseParameters response_parameters = 2;
+
+ // Optional input payload sent along with the request.
+ Payload payload = 3;
+
+ // Whether server should return a given status
+ EchoStatus response_status = 7;
+}
+
+// Server-streaming response, as configured by the request and parameters.
+message StreamingOutputCallResponse {
+ // Payload to increase response size.
+ Payload payload = 1;
+}
+
+// For reconnect interop test only.
+// Client tells server what reconnection parameters it used.
+message ReconnectParams {
+ int32 max_reconnect_backoff_ms = 1;
+}
+
+// For reconnect interop test only.
+// Server tells client whether its reconnects are following the spec and the
+// reconnect backoffs it saw.
+message ReconnectInfo {
+ bool passed = 1;
+ repeated int32 backoff_ms = 2;
+}
diff --git a/src/Grpc/test/testassets/Proto/grpc/testing/test.proto b/src/Grpc/test/testassets/Proto/grpc/testing/test.proto
new file mode 100644
index 000000000000..86d6ab60506a
--- /dev/null
+++ b/src/Grpc/test/testassets/Proto/grpc/testing/test.proto
@@ -0,0 +1,79 @@
+
+// Copyright 2015-2016 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.
+
+// An integration test service that covers all the method signature permutations
+// of unary/streaming requests/responses.
+
+syntax = "proto3";
+
+import "empty.proto";
+import "messages.proto";
+
+package grpc.testing;
+
+// A simple service to test the various types of RPCs and experiment with
+// performance with various types of payload.
+service TestService {
+ // One empty request followed by one empty response.
+ rpc EmptyCall(grpc.testing.Empty) returns (grpc.testing.Empty);
+
+ // One request followed by one response.
+ rpc UnaryCall(SimpleRequest) returns (SimpleResponse);
+
+ // One request followed by one response. Response has cache control
+ // headers set such that a caching HTTP proxy (such as GFE) can
+ // satisfy subsequent requests.
+ rpc CacheableUnaryCall(SimpleRequest) returns (SimpleResponse);
+
+ // One request followed by a sequence of responses (streamed download).
+ // The server returns the payload with client desired type and sizes.
+ rpc StreamingOutputCall(StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // A sequence of requests followed by one response (streamed upload).
+ // The server returns the aggregated size of client payload as the result.
+ rpc StreamingInputCall(stream StreamingInputCallRequest)
+ returns (StreamingInputCallResponse);
+
+ // A sequence of requests with each request served by the server immediately.
+ // As one request could lead to multiple responses, this interface
+ // demonstrates the idea of full duplexing.
+ rpc FullDuplexCall(stream StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // A sequence of requests followed by a sequence of responses.
+ // The server buffers all the client requests and then serves them in order. A
+ // stream of responses are returned to the client when the server starts with
+ // first request.
+ rpc HalfDuplexCall(stream StreamingOutputCallRequest)
+ returns (stream StreamingOutputCallResponse);
+
+ // The test server will not implement this method. It will be used
+ // to test the behavior when clients call unimplemented methods.
+ rpc UnimplementedCall(grpc.testing.Empty) returns (grpc.testing.Empty);
+}
+
+// A simple service NOT implemented at servers so clients can test for
+// that case.
+service UnimplementedService {
+ // A call that no server should implement
+ rpc UnimplementedCall(grpc.testing.Empty) returns (grpc.testing.Empty);
+}
+
+// A service used to control reconnect server.
+service ReconnectService {
+ rpc Start(grpc.testing.ReconnectParams) returns (grpc.testing.Empty);
+ rpc Stop(grpc.testing.Empty) returns (grpc.testing.ReconnectInfo);
+}
diff --git a/src/Grpc/test/testassets/README.md b/src/Grpc/test/testassets/README.md
new file mode 100644
index 000000000000..d7798c92695d
--- /dev/null
+++ b/src/Grpc/test/testassets/README.md
@@ -0,0 +1,3 @@
+InteropTestsClient and InteropTestsWebsite are copied from [grpc-dotnet](https://github.com/grpc/grpc-dotnet/tree/master/testassets).
+
+For more information about the interop tests, see the [interop tests specification](https://github.com/grpc/grpc/blob/master/doc/interop-test-descriptions.md).
diff --git a/src/ProjectTemplates/test/Helpers/AspNetProcess.cs b/src/ProjectTemplates/test/Helpers/AspNetProcess.cs
index 753eb1258a52..86724b970fb9 100644
--- a/src/ProjectTemplates/test/Helpers/AspNetProcess.cs
+++ b/src/ProjectTemplates/test/Helpers/AspNetProcess.cs
@@ -11,6 +11,7 @@
using AngleSharp.Dom.Html;
using AngleSharp.Parser.Html;
using Microsoft.AspNetCore.Certificates.Generation;
+using Microsoft.AspNetCore.Internal;
using Microsoft.AspNetCore.Server.IntegrationTesting;
using Microsoft.Extensions.CommandLineUtils;
using Microsoft.Extensions.Logging.Abstractions;
diff --git a/src/ProjectTemplates/test/Helpers/ErrorMessages.cs b/src/ProjectTemplates/test/Helpers/ErrorMessages.cs
index c63f008f831a..744ada299bfb 100644
--- a/src/ProjectTemplates/test/Helpers/ErrorMessages.cs
+++ b/src/ProjectTemplates/test/Helpers/ErrorMessages.cs
@@ -1,6 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
+using Microsoft.AspNetCore.Internal;
+
namespace Templates.Test.Helpers
{
internal static class ErrorMessages
diff --git a/src/ProjectTemplates/test/Helpers/Project.cs b/src/ProjectTemplates/test/Helpers/Project.cs
index 3fdcdf9d0ce2..b16801a5f748 100644
--- a/src/ProjectTemplates/test/Helpers/Project.cs
+++ b/src/ProjectTemplates/test/Helpers/Project.cs
@@ -10,6 +10,7 @@
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Internal;
using Microsoft.Extensions.CommandLineUtils;
using Xunit;
using Xunit.Abstractions;
diff --git a/src/ProjectTemplates/test/Helpers/TemplatePackageInstaller.cs b/src/ProjectTemplates/test/Helpers/TemplatePackageInstaller.cs
index 3eed004ef60a..5c94fde2b49b 100644
--- a/src/ProjectTemplates/test/Helpers/TemplatePackageInstaller.cs
+++ b/src/ProjectTemplates/test/Helpers/TemplatePackageInstaller.cs
@@ -8,6 +8,7 @@
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.AspNetCore.Internal;
using Microsoft.Extensions.CommandLineUtils;
using Xunit;
using Xunit.Abstractions;
diff --git a/src/ProjectTemplates/test/SpaTemplateTest/SpaTemplateTestBase.cs b/src/ProjectTemplates/test/SpaTemplateTest/SpaTemplateTestBase.cs
index bd393b8d5865..d702e01be1e6 100644
--- a/src/ProjectTemplates/test/SpaTemplateTest/SpaTemplateTestBase.cs
+++ b/src/ProjectTemplates/test/SpaTemplateTest/SpaTemplateTestBase.cs
@@ -10,6 +10,7 @@
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using Microsoft.AspNetCore.E2ETesting;
+using Microsoft.AspNetCore.Internal;
using Newtonsoft.Json.Linq;
using OpenQA.Selenium;
using Templates.Test.Helpers;
diff --git a/src/ProjectTemplates/test/Helpers/ProcessEx.cs b/src/Shared/Process/ProcessEx.cs
similarity index 99%
rename from src/ProjectTemplates/test/Helpers/ProcessEx.cs
rename to src/Shared/Process/ProcessEx.cs
index db132bdafe07..c1743a2f0a96 100644
--- a/src/ProjectTemplates/test/Helpers/ProcessEx.cs
+++ b/src/Shared/Process/ProcessEx.cs
@@ -15,7 +15,7 @@
using Microsoft.Extensions.Internal;
using Xunit.Abstractions;
-namespace Templates.Test.Helpers
+namespace Microsoft.AspNetCore.Internal
{
internal class ProcessEx : IDisposable
{
@@ -100,7 +100,7 @@ public static ProcessEx Run(ITestOutputHelper output, string workingDirectory, s
}
startInfo.EnvironmentVariables["NUGET_PACKAGES"] = NUGET_PACKAGES;
-
+
if (!string.IsNullOrEmpty(Environment.GetEnvironmentVariable("helix")))
{
startInfo.EnvironmentVariables["NUGET_FALLBACK_PACKAGES"] = Environment.GetEnvironmentVariable("NUGET_FALLBACK_PACKAGES");
@@ -195,7 +195,7 @@ public void WaitForExit(bool assertSuccess, TimeSpan? timeSpan = null)
}
}
- private static string GetNugetPackagesRestorePath() => (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("NUGET_RESTORE")))
+ private static string GetNugetPackagesRestorePath() => (string.IsNullOrEmpty(Environment.GetEnvironmentVariable("NUGET_RESTORE")))
? typeof(ProcessEx).Assembly
.GetCustomAttributes()
.First(attribute => attribute.Key == "TestPackageRestorePath")