Merge github.com:grpc/grpc into write_completion
diff --git a/CMakeLists.txt b/CMakeLists.txt
index efc33c0..b22aa18 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -421,6 +421,9 @@
 endif()
 add_dependencies(buildtests_c fake_resolver_test)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
+add_dependencies(buildtests_c fake_transport_security_test)
+endif()
+if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_c fd_conservation_posix_test)
 endif()
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
@@ -530,6 +533,9 @@
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 add_dependencies(buildtests_c socket_utils_test)
 endif()
+if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
+add_dependencies(buildtests_c ssl_transport_security_test)
+endif()
 add_dependencies(buildtests_c status_conversion_test)
 add_dependencies(buildtests_c stream_compression_test)
 add_dependencies(buildtests_c stream_owned_slice_test)
@@ -5966,6 +5972,39 @@
 if (gRPC_BUILD_TESTS)
 if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
 
+add_executable(fake_transport_security_test
+  test/core/tsi/fake_transport_security_test.c
+  test/core/tsi/transport_security_test_lib.c
+)
+
+
+target_include_directories(fake_transport_security_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${BORINGSSL_ROOT_DIR}/include
+  PRIVATE ${PROTOBUF_ROOT_DIR}/src
+  PRIVATE ${BENCHMARK_ROOT_DIR}/include
+  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
+  PRIVATE ${CARES_BUILD_INCLUDE_DIR}
+  PRIVATE ${CARES_INCLUDE_DIR}
+  PRIVATE ${CARES_PLATFORM_INCLUDE_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
+)
+
+target_link_libraries(fake_transport_security_test
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  gpr_test_util
+  gpr
+  grpc
+)
+
+endif()
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
+if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
+
 add_executable(fd_conservation_posix_test
   test/core/iomgr/fd_conservation_posix_test.c
 )
@@ -8711,6 +8750,39 @@
 endif()
 endif (gRPC_BUILD_TESTS)
 if (gRPC_BUILD_TESTS)
+if(_gRPC_PLATFORM_LINUX OR _gRPC_PLATFORM_MAC OR _gRPC_PLATFORM_POSIX)
+
+add_executable(ssl_transport_security_test
+  test/core/tsi/ssl_transport_security_test.c
+  test/core/tsi/transport_security_test_lib.c
+)
+
+
+target_include_directories(ssl_transport_security_test
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}
+  PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include
+  PRIVATE ${BORINGSSL_ROOT_DIR}/include
+  PRIVATE ${PROTOBUF_ROOT_DIR}/src
+  PRIVATE ${BENCHMARK_ROOT_DIR}/include
+  PRIVATE ${ZLIB_ROOT_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/zlib
+  PRIVATE ${CARES_BUILD_INCLUDE_DIR}
+  PRIVATE ${CARES_INCLUDE_DIR}
+  PRIVATE ${CARES_PLATFORM_INCLUDE_DIR}
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/cares/cares
+  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/third_party/gflags/include
+)
+
+target_link_libraries(ssl_transport_security_test
+  ${_gRPC_ALLTARGETS_LIBRARIES}
+  gpr_test_util
+  gpr
+  grpc
+)
+
+endif()
+endif (gRPC_BUILD_TESTS)
+if (gRPC_BUILD_TESTS)
 
 add_executable(status_conversion_test
   test/core/transport/status_conversion_test.c
diff --git a/Makefile b/Makefile
index 4fd534d..743ca41 100644
--- a/Makefile
+++ b/Makefile
@@ -976,6 +976,7 @@
 error_test: $(BINDIR)/$(CONFIG)/error_test
 ev_epollsig_linux_test: $(BINDIR)/$(CONFIG)/ev_epollsig_linux_test
 fake_resolver_test: $(BINDIR)/$(CONFIG)/fake_resolver_test
+fake_transport_security_test: $(BINDIR)/$(CONFIG)/fake_transport_security_test
 fd_conservation_posix_test: $(BINDIR)/$(CONFIG)/fd_conservation_posix_test
 fd_posix_test: $(BINDIR)/$(CONFIG)/fd_posix_test
 fling_client: $(BINDIR)/$(CONFIG)/fling_client
@@ -1075,6 +1076,7 @@
 sockaddr_utils_test: $(BINDIR)/$(CONFIG)/sockaddr_utils_test
 socket_utils_test: $(BINDIR)/$(CONFIG)/socket_utils_test
 ssl_server_fuzzer: $(BINDIR)/$(CONFIG)/ssl_server_fuzzer
+ssl_transport_security_test: $(BINDIR)/$(CONFIG)/ssl_transport_security_test
 status_conversion_test: $(BINDIR)/$(CONFIG)/status_conversion_test
 stream_compression_test: $(BINDIR)/$(CONFIG)/stream_compression_test
 stream_owned_slice_test: $(BINDIR)/$(CONFIG)/stream_owned_slice_test
@@ -1366,6 +1368,7 @@
   $(BINDIR)/$(CONFIG)/error_test \
   $(BINDIR)/$(CONFIG)/ev_epollsig_linux_test \
   $(BINDIR)/$(CONFIG)/fake_resolver_test \
+  $(BINDIR)/$(CONFIG)/fake_transport_security_test \
   $(BINDIR)/$(CONFIG)/fd_conservation_posix_test \
   $(BINDIR)/$(CONFIG)/fd_posix_test \
   $(BINDIR)/$(CONFIG)/fling_client \
@@ -1448,6 +1451,7 @@
   $(BINDIR)/$(CONFIG)/sockaddr_resolver_test \
   $(BINDIR)/$(CONFIG)/sockaddr_utils_test \
   $(BINDIR)/$(CONFIG)/socket_utils_test \
+  $(BINDIR)/$(CONFIG)/ssl_transport_security_test \
   $(BINDIR)/$(CONFIG)/status_conversion_test \
   $(BINDIR)/$(CONFIG)/stream_compression_test \
   $(BINDIR)/$(CONFIG)/stream_owned_slice_test \
@@ -1788,6 +1792,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/ev_epollsig_linux_test || ( echo test ev_epollsig_linux_test failed ; exit 1 )
 	$(E) "[RUN]     Testing fake_resolver_test"
 	$(Q) $(BINDIR)/$(CONFIG)/fake_resolver_test || ( echo test fake_resolver_test failed ; exit 1 )
+	$(E) "[RUN]     Testing fake_transport_security_test"
+	$(Q) $(BINDIR)/$(CONFIG)/fake_transport_security_test || ( echo test fake_transport_security_test failed ; exit 1 )
 	$(E) "[RUN]     Testing fd_conservation_posix_test"
 	$(Q) $(BINDIR)/$(CONFIG)/fd_conservation_posix_test || ( echo test fd_conservation_posix_test failed ; exit 1 )
 	$(E) "[RUN]     Testing fd_posix_test"
@@ -1936,6 +1942,8 @@
 	$(Q) $(BINDIR)/$(CONFIG)/sockaddr_utils_test || ( echo test sockaddr_utils_test failed ; exit 1 )
 	$(E) "[RUN]     Testing socket_utils_test"
 	$(Q) $(BINDIR)/$(CONFIG)/socket_utils_test || ( echo test socket_utils_test failed ; exit 1 )
+	$(E) "[RUN]     Testing ssl_transport_security_test"
+	$(Q) $(BINDIR)/$(CONFIG)/ssl_transport_security_test || ( echo test ssl_transport_security_test failed ; exit 1 )
 	$(E) "[RUN]     Testing status_conversion_test"
 	$(Q) $(BINDIR)/$(CONFIG)/status_conversion_test || ( echo test status_conversion_test failed ; exit 1 )
 	$(E) "[RUN]     Testing stream_compression_test"
@@ -9602,6 +9610,41 @@
 endif
 
 
+FAKE_TRANSPORT_SECURITY_TEST_SRC = \
+    test/core/tsi/fake_transport_security_test.c \
+    test/core/tsi/transport_security_test_lib.c \
+
+FAKE_TRANSPORT_SECURITY_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(FAKE_TRANSPORT_SECURITY_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/fake_transport_security_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/fake_transport_security_test: $(FAKE_TRANSPORT_SECURITY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(FAKE_TRANSPORT_SECURITY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/fake_transport_security_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/fake_transport_security_test.o:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/transport_security_test_lib.o:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_fake_transport_security_test: $(FAKE_TRANSPORT_SECURITY_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(FAKE_TRANSPORT_SECURITY_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 FD_CONSERVATION_POSIX_TEST_SRC = \
     test/core/iomgr/fd_conservation_posix_test.c \
 
@@ -12770,6 +12813,41 @@
 endif
 
 
+SSL_TRANSPORT_SECURITY_TEST_SRC = \
+    test/core/tsi/ssl_transport_security_test.c \
+    test/core/tsi/transport_security_test_lib.c \
+
+SSL_TRANSPORT_SECURITY_TEST_OBJS = $(addprefix $(OBJDIR)/$(CONFIG)/, $(addsuffix .o, $(basename $(SSL_TRANSPORT_SECURITY_TEST_SRC))))
+ifeq ($(NO_SECURE),true)
+
+# You can't build secure targets if you don't have OpenSSL.
+
+$(BINDIR)/$(CONFIG)/ssl_transport_security_test: openssl_dep_error
+
+else
+
+
+
+$(BINDIR)/$(CONFIG)/ssl_transport_security_test: $(SSL_TRANSPORT_SECURITY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+	$(E) "[LD]      Linking $@"
+	$(Q) mkdir -p `dirname $@`
+	$(Q) $(LD) $(LDFLAGS) $(SSL_TRANSPORT_SECURITY_TEST_OBJS) $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a $(LDLIBS) $(LDLIBS_SECURE) -o $(BINDIR)/$(CONFIG)/ssl_transport_security_test
+
+endif
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/ssl_transport_security_test.o:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+$(OBJDIR)/$(CONFIG)/test/core/tsi/transport_security_test_lib.o:  $(LIBDIR)/$(CONFIG)/libgpr_test_util.a $(LIBDIR)/$(CONFIG)/libgpr.a $(LIBDIR)/$(CONFIG)/libgrpc.a
+
+deps_ssl_transport_security_test: $(SSL_TRANSPORT_SECURITY_TEST_OBJS:.o=.dep)
+
+ifneq ($(NO_SECURE),true)
+ifneq ($(NO_DEPS),true)
+-include $(SSL_TRANSPORT_SECURITY_TEST_OBJS:.o=.dep)
+endif
+endif
+
+
 STATUS_CONVERSION_TEST_SRC = \
     test/core/transport/status_conversion_test.c \
 
diff --git a/WORKSPACE b/WORKSPACE
index 82ea99f..bfb3a8c 100644
--- a/WORKSPACE
+++ b/WORKSPACE
@@ -39,6 +39,11 @@
 )
 
 bind(
+    name = "gmock",
+    actual = "@submodule_gtest//:gmock",
+)
+
+bind(
     name = "benchmark",
     actual = "@submodule_benchmark//:benchmark",
 )
diff --git a/build.yaml b/build.yaml
index a43ad6a..7d8440c 100644
--- a/build.yaml
+++ b/build.yaml
@@ -750,6 +750,7 @@
   - test/core/util/trickle_endpoint.c
   deps:
   - gpr_test_util
+  - gpr
   uses:
   - grpc_base
   - grpc_client_channel
@@ -924,6 +925,14 @@
   - third_party/nanopb/pb_common.h
   - third_party/nanopb/pb_decode.h
   - third_party/nanopb/pb_encode.h
+- name: transport_security_test_lib
+  build: test
+  headers:
+  - test/core/tsi/transport_security_test_lib.h
+  src:
+  - test/core/tsi/transport_security_test_lib.c
+  deps:
+  - grpc
 - name: tsi
   headers:
   - src/core/tsi/fake_transport_security.h
@@ -2034,6 +2043,21 @@
   - grpc
   - gpr_test_util
   - gpr
+- name: fake_transport_security_test
+  build: test
+  language: c
+  src:
+  - test/core/tsi/fake_transport_security_test.c
+  deps:
+  - gpr_test_util
+  - gpr
+  - grpc
+  filegroups:
+  - transport_security_test_lib
+  platforms:
+  - linux
+  - posix
+  - mac
 - name: fd_conservation_posix_test
   build: test
   language: c
@@ -3105,6 +3129,21 @@
   corpus_dirs:
   - test/core/security/corpus/ssl_server_corpus
   maxlen: 2048
+- name: ssl_transport_security_test
+  build: test
+  language: c
+  src:
+  - test/core/tsi/ssl_transport_security_test.c
+  deps:
+  - gpr_test_util
+  - gpr
+  - grpc
+  filegroups:
+  - transport_security_test_lib
+  platforms:
+  - linux
+  - posix
+  - mac
 - name: status_conversion_test
   build: test
   language: c
diff --git a/gRPC-Core.podspec b/gRPC-Core.podspec
index e82f917..66368b1 100644
--- a/gRPC-Core.podspec
+++ b/gRPC-Core.podspec
@@ -994,6 +994,7 @@
                       'test/core/end2end/end2end_tests.{c,h}',
                       'test/core/end2end/end2end_test_utils.c',
                       'test/core/end2end/tests/*.{c,h}',
+                      'test/core/end2end/fixtures/*.h',
                       'test/core/end2end/data/*.{c,h}',
                       'test/core/util/debugger_macros.{c,h}',
                       'test/core/util/test_config.{c,h}',
diff --git a/grpc.gemspec b/grpc.gemspec
index 2b41f13..d3779a9 100644
--- a/grpc.gemspec
+++ b/grpc.gemspec
@@ -33,12 +33,12 @@
   s.add_development_dependency 'bundler',            '~> 1.9'
   s.add_development_dependency 'facter',             '~> 2.4'
   s.add_development_dependency 'logging',            '~> 2.0'
-  s.add_development_dependency 'simplecov',          '~> 0.9'
-  s.add_development_dependency 'rake',               '~> 10.4'
+  s.add_development_dependency 'simplecov',          '~> 0.14.1'
+  s.add_development_dependency 'rake',               '~> 12.0'
   s.add_development_dependency 'rake-compiler',      '~> 1.0'
   s.add_development_dependency 'rake-compiler-dock', '~> 0.5.1'
-  s.add_development_dependency 'rspec',              '~> 3.2'
-  s.add_development_dependency 'rubocop',            '~> 0.30.0'
+  s.add_development_dependency 'rspec',              '~> 3.6'
+  s.add_development_dependency 'rubocop',            '~> 0.49.1'
   s.add_development_dependency 'signet',             '~> 0.7.0'
 
   s.extensions = %w(src/ruby/ext/grpc/extconf.rb)
diff --git a/src/core/ext/transport/chttp2/transport/writing.c b/src/core/ext/transport/chttp2/transport/writing.c
index 029426b..8abddd7 100644
--- a/src/core/ext/transport/chttp2/transport/writing.c
+++ b/src/core/ext/transport/chttp2/transport/writing.c
@@ -158,17 +158,8 @@
 }
 
 // Returns true if initial_metadata contains only default headers.
-//
-// TODO(roth): The fact that we hard-code these particular headers here
-// is fairly ugly.  Need some better way to know which headers are
-// default, maybe via a bit in the static metadata table?
 static bool is_default_initial_metadata(grpc_metadata_batch *initial_metadata) {
-  int num_default_fields =
-      (initial_metadata->idx.named.status != NULL) +
-      (initial_metadata->idx.named.content_type != NULL) +
-      (initial_metadata->idx.named.grpc_encoding != NULL) +
-      (initial_metadata->idx.named.grpc_accept_encoding != NULL);
-  return (size_t)num_default_fields == initial_metadata->list.count;
+  return initial_metadata->list.default_count == initial_metadata->list.count;
 }
 
 grpc_chttp2_begin_write_result grpc_chttp2_begin_write(
diff --git a/src/core/ext/transport/cronet/transport/cronet_transport.c b/src/core/ext/transport/cronet/transport/cronet_transport.c
index abb5589..765c13c 100644
--- a/src/core/ext/transport/cronet/transport/cronet_transport.c
+++ b/src/core/ext/transport/cronet/transport/cronet_transport.c
@@ -187,9 +187,34 @@
 
   /* Mutex to protect storage */
   gpr_mu mu;
+
+  /* Refcount object of the stream */
+  grpc_stream_refcount *refcount;
 };
 typedef struct stream_obj stream_obj;
 
+#ifndef NDEBUG
+#define GRPC_CRONET_STREAM_REF(stream, reason) \
+  grpc_cronet_stream_ref((stream), (reason))
+#define GRPC_CRONET_STREAM_UNREF(exec_ctx, stream, reason) \
+  grpc_cronet_stream_unref((exec_ctx), (stream), (reason))
+void grpc_cronet_stream_ref(stream_obj *s, const char *reason) {
+  grpc_stream_ref(s->refcount, reason);
+}
+void grpc_cronet_stream_unref(grpc_exec_ctx *exec_ctx, stream_obj *s,
+                              const char *reason) {
+  grpc_stream_unref(exec_ctx, s->refcount, reason);
+}
+#else
+#define GRPC_CRONET_STREAM_REF(stream, reason) grpc_cronet_stream_ref((stream))
+#define GRPC_CRONET_STREAM_UNREF(exec_ctx, stream, reason) \
+  grpc_cronet_stream_unref((exec_ctx), (stream))
+void grpc_cronet_stream_ref(stream_obj *s) { grpc_stream_ref(s->refcount); }
+void grpc_cronet_stream_unref(grpc_exec_ctx *exec_ctx, stream_obj *s) {
+  grpc_stream_unref(exec_ctx, s->refcount);
+}
+#endif
+
 static enum e_op_result execute_stream_op(grpc_exec_ctx *exec_ctx,
                                           struct op_and_state *oas);
 
@@ -346,13 +371,12 @@
   This can get executed from the Cronet network thread via cronet callback
   or on the application supplied thread via the perform_stream_op function.
 */
-static void execute_from_storage(stream_obj *s) {
-  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+static void execute_from_storage(grpc_exec_ctx *exec_ctx, stream_obj *s) {
   gpr_mu_lock(&s->mu);
   for (struct op_and_state *curr = s->storage.head; curr != NULL;) {
     CRONET_LOG(GPR_DEBUG, "calling op at %p. done = %d", curr, curr->done);
     GPR_ASSERT(curr->done == 0);
-    enum e_op_result result = execute_stream_op(&exec_ctx, curr);
+    enum e_op_result result = execute_stream_op(exec_ctx, curr);
     CRONET_LOG(GPR_DEBUG, "execute_stream_op[%p] returns %s", curr,
                op_result_string(result));
     /* if this op is done, then remove it and free memory */
@@ -369,7 +393,6 @@
     }
   }
   gpr_mu_unlock(&s->mu);
-  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 /*
@@ -377,6 +400,8 @@
 */
 static void on_failed(bidirectional_stream *stream, int net_error) {
   CRONET_LOG(GPR_DEBUG, "on_failed(%p, %d)", stream, net_error);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
   stream_obj *s = (stream_obj *)stream->annotation;
   gpr_mu_lock(&s->mu);
   bidirectional_stream_destroy(s->cbs);
@@ -392,7 +417,9 @@
   }
   null_and_maybe_free_read_buffer(s);
   gpr_mu_unlock(&s->mu);
-  execute_from_storage(s);
+  execute_from_storage(&exec_ctx, s);
+  GRPC_CRONET_STREAM_UNREF(&exec_ctx, s, "cronet transport");
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 /*
@@ -400,6 +427,8 @@
 */
 static void on_canceled(bidirectional_stream *stream) {
   CRONET_LOG(GPR_DEBUG, "on_canceled(%p)", stream);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
   stream_obj *s = (stream_obj *)stream->annotation;
   gpr_mu_lock(&s->mu);
   bidirectional_stream_destroy(s->cbs);
@@ -415,7 +444,9 @@
   }
   null_and_maybe_free_read_buffer(s);
   gpr_mu_unlock(&s->mu);
-  execute_from_storage(s);
+  execute_from_storage(&exec_ctx, s);
+  GRPC_CRONET_STREAM_UNREF(&exec_ctx, s, "cronet transport");
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 /*
@@ -423,6 +454,8 @@
 */
 static void on_succeeded(bidirectional_stream *stream) {
   CRONET_LOG(GPR_DEBUG, "on_succeeded(%p)", stream);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
+
   stream_obj *s = (stream_obj *)stream->annotation;
   gpr_mu_lock(&s->mu);
   bidirectional_stream_destroy(s->cbs);
@@ -430,7 +463,9 @@
   s->cbs = NULL;
   null_and_maybe_free_read_buffer(s);
   gpr_mu_unlock(&s->mu);
-  execute_from_storage(s);
+  execute_from_storage(&exec_ctx, s);
+  GRPC_CRONET_STREAM_UNREF(&exec_ctx, s, "cronet transport");
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 /*
@@ -438,6 +473,7 @@
 */
 static void on_stream_ready(bidirectional_stream *stream) {
   CRONET_LOG(GPR_DEBUG, "W: on_stream_ready(%p)", stream);
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   stream_obj *s = (stream_obj *)stream->annotation;
   grpc_cronet_transport *t = (grpc_cronet_transport *)s->curr_ct;
   gpr_mu_lock(&s->mu);
@@ -457,7 +493,8 @@
     }
   }
   gpr_mu_unlock(&s->mu);
-  execute_from_storage(s);
+  execute_from_storage(&exec_ctx, s);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 /*
@@ -513,14 +550,15 @@
     s->state.pending_read_from_cronet = true;
   }
   gpr_mu_unlock(&s->mu);
+  execute_from_storage(&exec_ctx, s);
   grpc_exec_ctx_finish(&exec_ctx);
-  execute_from_storage(s);
 }
 
 /*
   Cronet callback
 */
 static void on_write_completed(bidirectional_stream *stream, const char *data) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   stream_obj *s = (stream_obj *)stream->annotation;
   CRONET_LOG(GPR_DEBUG, "W: on_write_completed(%p, %s)", stream, data);
   gpr_mu_lock(&s->mu);
@@ -530,7 +568,8 @@
   }
   s->state.state_callback_received[OP_SEND_MESSAGE] = true;
   gpr_mu_unlock(&s->mu);
-  execute_from_storage(s);
+  execute_from_storage(&exec_ctx, s);
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 /*
@@ -538,6 +577,7 @@
 */
 static void on_read_completed(bidirectional_stream *stream, char *data,
                               int count) {
+  grpc_exec_ctx exec_ctx = GRPC_EXEC_CTX_INIT;
   stream_obj *s = (stream_obj *)stream->annotation;
   CRONET_LOG(GPR_DEBUG, "R: on_read_completed(%p, %p, %d)", stream, data,
              count);
@@ -563,14 +603,15 @@
       gpr_mu_unlock(&s->mu);
     } else {
       gpr_mu_unlock(&s->mu);
-      execute_from_storage(s);
+      execute_from_storage(&exec_ctx, s);
     }
   } else {
     null_and_maybe_free_read_buffer(s);
     s->state.rs.read_stream_closed = true;
     gpr_mu_unlock(&s->mu);
-    execute_from_storage(s);
+    execute_from_storage(&exec_ctx, s);
   }
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 /*
@@ -625,12 +666,11 @@
     s->state.state_op_done[OP_SEND_TRAILING_METADATA] = true;
 
     gpr_mu_unlock(&s->mu);
-    grpc_exec_ctx_finish(&exec_ctx);
   } else {
     gpr_mu_unlock(&s->mu);
-    grpc_exec_ctx_finish(&exec_ctx);
-    execute_from_storage(s);
+    execute_from_storage(&exec_ctx, s);
   }
+  grpc_exec_ctx_finish(&exec_ctx);
 }
 
 /*
@@ -1313,6 +1353,9 @@
                        grpc_stream *gs, grpc_stream_refcount *refcount,
                        const void *server_data, gpr_arena *arena) {
   stream_obj *s = (stream_obj *)gs;
+
+  s->refcount = refcount;
+  GRPC_CRONET_STREAM_REF(s, "cronet transport");
   memset(&s->storage, 0, sizeof(s->storage));
   s->storage.head = NULL;
   memset(&s->state, 0, sizeof(s->state));
@@ -1370,7 +1413,7 @@
   }
   stream_obj *s = (stream_obj *)gs;
   add_to_storage(s, op);
-  execute_from_storage(s);
+  execute_from_storage(exec_ctx, s);
 }
 
 static void destroy_stream(grpc_exec_ctx *exec_ctx, grpc_transport *gt,
diff --git a/src/core/lib/transport/metadata_batch.c b/src/core/lib/transport/metadata_batch.c
index 8f24b85..a077052 100644
--- a/src/core/lib/transport/metadata_batch.c
+++ b/src/core/lib/transport/metadata_batch.c
@@ -105,6 +105,7 @@
     return GRPC_ERROR_NONE;
   }
   if (batch->idx.array[idx] == NULL) {
+    if (grpc_static_callout_is_default[idx]) ++batch->list.default_count;
     batch->idx.array[idx] = storage;
     return GRPC_ERROR_NONE;
   }
@@ -120,6 +121,7 @@
   if (idx == GRPC_BATCH_CALLOUTS_COUNT) {
     return;
   }
+  if (grpc_static_callout_is_default[idx]) --batch->list.default_count;
   GPR_ASSERT(batch->idx.array[idx] != NULL);
   batch->idx.array[idx] = NULL;
 }
diff --git a/src/core/lib/transport/metadata_batch.h b/src/core/lib/transport/metadata_batch.h
index 1b11a3e..57d298c 100644
--- a/src/core/lib/transport/metadata_batch.h
+++ b/src/core/lib/transport/metadata_batch.h
@@ -41,6 +41,7 @@
 
 typedef struct grpc_mdelem_list {
   size_t count;
+  size_t default_count;  // Number of default keys.
   grpc_linked_mdelem *head;
   grpc_linked_mdelem *tail;
 } grpc_mdelem_list;
diff --git a/src/core/lib/transport/static_metadata.c b/src/core/lib/transport/static_metadata.c
index 28f05d5..b20d94a 100644
--- a/src/core/lib/transport/static_metadata.c
+++ b/src/core/lib/transport/static_metadata.c
@@ -823,6 +823,31 @@
      {.refcount = &grpc_static_metadata_refcounts[97],
       .data.refcounted = {g_bytes + 1040, 13}}},
 };
+bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT] = {
+    true,  // :path
+    true,  // :method
+    true,  // :status
+    true,  // :authority
+    true,  // :scheme
+    true,  // te
+    true,  // grpc-message
+    true,  // grpc-status
+    true,  // grpc-payload-bin
+    true,  // grpc-encoding
+    true,  // grpc-accept-encoding
+    true,  // grpc-server-stats-bin
+    true,  // grpc-tags-bin
+    true,  // grpc-trace-bin
+    true,  // content-type
+    true,  // content-encoding
+    true,  // accept-encoding
+    true,  // grpc-internal-encoding-request
+    true,  // grpc-internal-stream-encoding-request
+    true,  // user-agent
+    true,  // host
+    true,  // lb-token
+};
+
 const uint8_t grpc_static_accept_encoding_metadata[8] = {0,  76, 77, 78,
                                                          79, 80, 81, 82};
 
diff --git a/src/core/lib/transport/static_metadata.h b/src/core/lib/transport/static_metadata.h
index 93ab90d..f03a9d2 100644
--- a/src/core/lib/transport/static_metadata.h
+++ b/src/core/lib/transport/static_metadata.h
@@ -571,6 +571,8 @@
              GRPC_BATCH_CALLOUTS_COUNT)                 \
        : GRPC_BATCH_CALLOUTS_COUNT)
 
+extern bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT];
+
 extern const uint8_t grpc_static_accept_encoding_metadata[8];
 #define GRPC_MDELEM_ACCEPT_ENCODING_FOR_ALGORITHMS(algs)                       \
   (GRPC_MAKE_MDELEM(                                                           \
diff --git a/src/core/tsi/test_creds/BUILD b/src/core/tsi/test_creds/BUILD
index 4b0786d..732f6d9 100644
--- a/src/core/tsi/test_creds/BUILD
+++ b/src/core/tsi/test_creds/BUILD
@@ -15,7 +15,15 @@
 licenses(["notice"])  # Apache v2
 
 exports_files([
-    "ca.pem",
-    "server1.key",
-    "server1.pem",
+        "ca.pem",
+        "server1.key",
+        "server1.pem",
+        "server0.key",
+        "server0.pem",
+        "client.key",
+        "client.pem",
+        "badserver.key",
+        "badserver.pem",
+        "badclient.key",
+        "badclient.pem",
 ])
diff --git "a/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec" "b/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
index 3a282b0..7d073c9 100644
--- "a/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
+++ "b/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec"
@@ -101,7 +101,7 @@
   s.preserve_paths = plugin
 
   # Restrict the protoc version to the one supported by this plugin.
-  s.dependency '!ProtoCompiler', '3.3.0'
+  s.dependency '!ProtoCompiler', '3.4.0'
   # For the Protobuf dependency not to complain:
   s.ios.deployment_target = '7.0'
   s.osx.deployment_target = '10.9'
diff --git "a/src/objective-c/\041ProtoCompiler.podspec" "b/src/objective-c/\041ProtoCompiler.podspec"
index c3f95f9..25c4379 100644
--- "a/src/objective-c/\041ProtoCompiler.podspec"
+++ "b/src/objective-c/\041ProtoCompiler.podspec"
@@ -36,7 +36,7 @@
   # exclamation mark ensures that other "regular" pods will be able to find it as it'll be installed
   # before them.
   s.name     = '!ProtoCompiler'
-  v = '3.3.0'
+  v = '3.4.0'
   s.version  = v
   s.summary  = 'The Protobuf Compiler (protoc) generates Objective-C files from .proto files'
   s.description = <<-DESC
diff --git a/src/python/grpcio_testing/grpc_testing/__init__.py b/src/python/grpcio_testing/grpc_testing/__init__.py
index 14e25f0..917e118 100644
--- a/src/python/grpcio_testing/grpc_testing/__init__.py
+++ b/src/python/grpcio_testing/grpc_testing/__init__.py
@@ -293,6 +293,278 @@
         raise NotImplementedError()
 
 
+class UnaryUnaryServerRpc(six.with_metaclass(abc.ABCMeta)):
+    """Fixture for a unary-unary RPC serviced by a system under test.
+
+    Enables users to "play client" for the RPC.
+    """
+
+    @abc.abstractmethod
+    def initial_metadata(self):
+        """Accesses the initial metadata emitted by the system under test.
+
+        This method blocks until the system under test has added initial
+        metadata to the RPC (or has provided one or more response messages or
+        has terminated the RPC, either of which will cause gRPC Python to
+        synthesize initial metadata for the RPC).
+
+        Returns:
+          The initial metadata for the RPC.
+        """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def cancel(self):
+        """Cancels the RPC."""
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def termination(self):
+        """Blocks until the system under test has terminated the RPC.
+
+        Returns:
+          A (response, trailing_metadata, code, details) sequence with the RPC's
+            response, trailing metadata, code, and details.
+        """
+        raise NotImplementedError()
+
+
+class UnaryStreamServerRpc(six.with_metaclass(abc.ABCMeta)):
+    """Fixture for a unary-stream RPC serviced by a system under test.
+
+    Enables users to "play client" for the RPC.
+    """
+
+    @abc.abstractmethod
+    def initial_metadata(self):
+        """Accesses the initial metadata emitted by the system under test.
+
+        This method blocks until the system under test has added initial
+        metadata to the RPC (or has provided one or more response messages or
+        has terminated the RPC, either of which will cause gRPC Python to
+        synthesize initial metadata for the RPC).
+
+        Returns:
+          The initial metadata for the RPC.
+        """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def take_response(self):
+        """Draws one of the responses added to the RPC by the system under test.
+
+        Successive calls to this method return responses in the same order in
+        which the system under test added them to the RPC.
+
+        Returns:
+          A response message added to the RPC by the system under test.
+        """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def cancel(self):
+        """Cancels the RPC."""
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def termination(self):
+        """Blocks until the system under test has terminated the RPC.
+
+        Returns:
+          A (trailing_metadata, code, details) sequence with the RPC's trailing
+            metadata, code, and details.
+        """
+        raise NotImplementedError()
+
+
+class StreamUnaryServerRpc(six.with_metaclass(abc.ABCMeta)):
+    """Fixture for a stream-unary RPC serviced by a system under test.
+
+    Enables users to "play client" for the RPC.
+    """
+
+    @abc.abstractmethod
+    def initial_metadata(self):
+        """Accesses the initial metadata emitted by the system under test.
+
+        This method blocks until the system under test has added initial
+        metadata to the RPC (or has provided one or more response messages or
+        has terminated the RPC, either of which will cause gRPC Python to
+        synthesize initial metadata for the RPC).
+
+        Returns:
+          The initial metadata for the RPC.
+        """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def send_request(self, request):
+        """Sends a request to the system under test.
+
+        Args:
+          request: A request message for the RPC to be "sent" to the system
+            under test.
+        """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def requests_closed(self):
+        """Indicates the end of the RPC's request stream."""
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def cancel(self):
+        """Cancels the RPC."""
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def termination(self):
+        """Blocks until the system under test has terminated the RPC.
+
+        Returns:
+          A (response, trailing_metadata, code, details) sequence with the RPC's
+            response, trailing metadata, code, and details.
+        """
+        raise NotImplementedError()
+
+
+class StreamStreamServerRpc(six.with_metaclass(abc.ABCMeta)):
+    """Fixture for a stream-stream RPC serviced by a system under test.
+
+    Enables users to "play client" for the RPC.
+    """
+
+    @abc.abstractmethod
+    def initial_metadata(self):
+        """Accesses the initial metadata emitted by the system under test.
+
+        This method blocks until the system under test has added initial
+        metadata to the RPC (or has provided one or more response messages or
+        has terminated the RPC, either of which will cause gRPC Python to
+        synthesize initial metadata for the RPC).
+
+        Returns:
+          The initial metadata for the RPC.
+        """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def send_request(self, request):
+        """Sends a request to the system under test.
+
+        Args:
+          request: A request message for the RPC to be "sent" to the system
+            under test.
+        """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def requests_closed(self):
+        """Indicates the end of the RPC's request stream."""
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def take_response(self):
+        """Draws one of the responses added to the RPC by the system under test.
+
+        Successive calls to this method return responses in the same order in
+        which the system under test added them to the RPC.
+
+        Returns:
+          A response message added to the RPC by the system under test.
+        """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def cancel(self):
+        """Cancels the RPC."""
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def termination(self):
+        """Blocks until the system under test has terminated the RPC.
+
+        Returns:
+          A (trailing_metadata, code, details) sequence with the RPC's trailing
+            metadata, code, and details.
+        """
+        raise NotImplementedError()
+
+
+class Server(six.with_metaclass(abc.ABCMeta)):
+    """A server with which to test a system that services RPCs."""
+
+    @abc.abstractmethod
+    def invoke_unary_unary(
+            self, method_descriptor, invocation_metadata, request, timeout):
+        """Invokes an RPC to be serviced by the system under test.
+
+        Args:
+          method_descriptor: A descriptor.MethodDescriptor describing a unary-unary
+            RPC method.
+          invocation_metadata: The RPC's invocation metadata.
+          request: The RPC's request.
+          timeout: A duration of time in seconds for the RPC or None to
+            indicate that the RPC has no time limit.
+
+        Returns:
+          A UnaryUnaryServerRpc with which to "play client" for the RPC.
+        """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def invoke_unary_stream(
+            self, method_descriptor, invocation_metadata, request, timeout):
+        """Invokes an RPC to be serviced by the system under test.
+
+        Args:
+          method_descriptor: A descriptor.MethodDescriptor describing a unary-stream
+            RPC method.
+          invocation_metadata: The RPC's invocation metadata.
+          request: The RPC's request.
+          timeout: A duration of time in seconds for the RPC or None to
+            indicate that the RPC has no time limit.
+
+        Returns:
+          A UnaryStreamServerRpc with which to "play client" for the RPC.
+        """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def invoke_stream_unary(
+            self, method_descriptor, invocation_metadata, timeout):
+        """Invokes an RPC to be serviced by the system under test.
+
+        Args:
+          method_descriptor: A descriptor.MethodDescriptor describing a stream-unary
+            RPC method.
+          invocation_metadata: The RPC's invocation metadata.
+          timeout: A duration of time in seconds for the RPC or None to
+            indicate that the RPC has no time limit.
+
+        Returns:
+          A StreamUnaryServerRpc with which to "play client" for the RPC.
+        """
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def invoke_stream_stream(
+            self, method_descriptor, invocation_metadata, timeout):
+        """Invokes an RPC to be serviced by the system under test.
+
+        Args:
+          method_descriptor: A descriptor.MethodDescriptor describing a stream-stream
+            RPC method.
+          invocation_metadata: The RPC's invocation metadata.
+          timeout: A duration of time in seconds for the RPC or None to
+            indicate that the RPC has no time limit.
+
+        Returns:
+          A StreamStreamServerRpc with which to "play client" for the RPC.
+        """
+        raise NotImplementedError()
+
+
 class Time(six.with_metaclass(abc.ABCMeta)):
     """A simulation of time.
 
@@ -406,3 +678,20 @@
     """
     from grpc_testing import _channel
     return _channel.testing_channel(service_descriptors, time)
+
+
+def server_from_dictionary(descriptors_to_servicers, time):
+    """Creates a Server for use in tests of a gRPC Python-using system.
+
+    Args:
+      descriptors_to_servicers: A dictionary from descriptor.ServiceDescriptors
+        defining RPC services to servicer objects (usually instances of classes
+        that implement "Servicer" interfaces defined in generated "_pb2_grpc"
+        modules) implementing those services.
+      time: A Time to be used for tests.
+
+    Returns:
+      A Server for use in tests.
+    """
+    from grpc_testing import _server
+    return _server.server_from_dictionary(descriptors_to_servicers, time)
diff --git a/src/python/grpcio_testing/grpc_testing/_common.py b/src/python/grpcio_testing/grpc_testing/_common.py
index cb4a7f5..1517434 100644
--- a/src/python/grpcio_testing/grpc_testing/_common.py
+++ b/src/python/grpcio_testing/grpc_testing/_common.py
@@ -37,6 +37,16 @@
         return _fuss(tuple(metadata))
 
 
+def rpc_names(service_descriptors):
+    rpc_names_to_descriptors = {}
+    for service_descriptor in service_descriptors:
+        for method_descriptor in service_descriptor.methods_by_name.values():
+            rpc_name = '/{}/{}'.format(
+                service_descriptor.full_name, method_descriptor.name)
+            rpc_names_to_descriptors[rpc_name] = method_descriptor
+    return rpc_names_to_descriptors
+
+
 class ChannelRpcRead(
         collections.namedtuple(
             'ChannelRpcRead',
@@ -90,3 +100,61 @@
             self, method_full_rpc_name, invocation_metadata, requests,
             requests_closed, timeout):
         raise NotImplementedError()
+
+
+class ServerRpcRead(
+        collections.namedtuple('ServerRpcRead',
+                               ('request', 'requests_closed', 'terminated',))):
+    pass
+
+
+REQUESTS_CLOSED = ServerRpcRead(None, True, False)
+TERMINATED = ServerRpcRead(None, False, True)
+
+
+class ServerRpcHandler(six.with_metaclass(abc.ABCMeta)):
+
+    @abc.abstractmethod
+    def send_initial_metadata(self, initial_metadata):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def take_request(self):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def add_response(self, response):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def send_termination(self, trailing_metadata, code, details):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def add_termination_callback(self, callback):
+        raise NotImplementedError()
+
+
+class Serverish(six.with_metaclass(abc.ABCMeta)):
+
+    @abc.abstractmethod
+    def invoke_unary_unary(
+            self, method_descriptor, handler, invocation_metadata, request,
+            deadline):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def invoke_unary_stream(
+            self, method_descriptor, handler, invocation_metadata, request,
+            deadline):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def invoke_stream_unary(
+            self, method_descriptor, handler, invocation_metadata, deadline):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def invoke_stream_stream(
+            self, method_descriptor, handler, invocation_metadata, deadline):
+        raise NotImplementedError()
diff --git a/src/python/grpcio_testing/grpc_testing/_server/__init__.py b/src/python/grpcio_testing/grpc_testing/_server/__init__.py
new file mode 100644
index 0000000..7595129
--- /dev/null
+++ b/src/python/grpcio_testing/grpc_testing/_server/__init__.py
@@ -0,0 +1,20 @@
+# Copyright 2017 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.
+
+from grpc_testing._server import _server
+
+
+def server_from_dictionary(descriptors_to_servicers, time):
+    return _server.server_from_descriptor_to_servicers(
+        descriptors_to_servicers, time)
diff --git a/src/python/grpcio_testing/grpc_testing/_server/_handler.py b/src/python/grpcio_testing/grpc_testing/_server/_handler.py
new file mode 100644
index 0000000..b47e04c
--- /dev/null
+++ b/src/python/grpcio_testing/grpc_testing/_server/_handler.py
@@ -0,0 +1,215 @@
+# Copyright 2017 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.
+
+import abc
+import threading
+
+import grpc
+from grpc_testing import _common
+
+_CLIENT_INACTIVE = object()
+
+
+class Handler(_common.ServerRpcHandler):
+
+    @abc.abstractmethod
+    def initial_metadata(self):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def add_request(self, request):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def take_response(self):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def requests_closed(self):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def cancel(self):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def unary_response_termination(self):
+        raise NotImplementedError()
+
+    @abc.abstractmethod
+    def stream_response_termination(self):
+        raise NotImplementedError()
+
+
+class _Handler(Handler):
+
+    def __init__(self, requests_closed):
+        self._condition = threading.Condition()
+        self._requests = []
+        self._requests_closed = requests_closed
+        self._initial_metadata = None
+        self._responses = []
+        self._trailing_metadata = None
+        self._code = None
+        self._details = None
+        self._unary_response = None
+        self._expiration_future = None
+        self._termination_callbacks = []
+
+    def send_initial_metadata(self, initial_metadata):
+        with self._condition:
+            self._initial_metadata = initial_metadata
+            self._condition.notify_all()
+
+    def take_request(self):
+        with self._condition:
+            while True:
+                if self._code is None:
+                    if self._requests:
+                        request = self._requests.pop(0)
+                        self._condition.notify_all()
+                        return _common.ServerRpcRead(request, False, False)
+                    elif self._requests_closed:
+                        return _common.REQUESTS_CLOSED
+                    else:
+                        self._condition.wait()
+                else:
+                    return _common.TERMINATED
+
+    def is_active(self):
+        with self._condition:
+            return self._code is None
+
+    def add_response(self, response):
+        with self._condition:
+            self._responses.append(response)
+            self._condition.notify_all()
+
+    def send_termination(self, trailing_metadata, code, details):
+        with self._condition:
+            self._trailing_metadata = trailing_metadata
+            self._code = code
+            self._details = details
+            if self._expiration_future is not None:
+                self._expiration_future.cancel()
+            self._condition.notify_all()
+
+    def add_termination_callback(self, termination_callback):
+        with self._condition:
+            if self._code is None:
+                self._termination_callbacks.append(termination_callback)
+                return True
+            else:
+                return False
+
+    def initial_metadata(self):
+        with self._condition:
+            while True:
+                if self._initial_metadata is None:
+                    if self._code is None:
+                        self._condition.wait()
+                    else:
+                        raise ValueError(
+                            'No initial metadata despite status code!')
+                else:
+                    return self._initial_metadata
+
+    def add_request(self, request):
+        with self._condition:
+            self._requests.append(request)
+            self._condition.notify_all()
+
+    def take_response(self):
+        with self._condition:
+            while True:
+                if self._responses:
+                    response = self._responses.pop(0)
+                    self._condition.notify_all()
+                    return response
+                elif self._code is None:
+                    self._condition.wait()
+                else:
+                    raise ValueError('No more responses!')
+
+    def requests_closed(self):
+        with self._condition:
+            self._requests_closed = True
+            self._condition.notify_all()
+
+    def cancel(self):
+        with self._condition:
+            if self._code is None:
+                self._code = _CLIENT_INACTIVE
+                termination_callbacks = self._termination_callbacks
+                self._termination_callbacks = None
+                if self._expiration_future is not None:
+                    self._expiration_future.cancel()
+                self._condition.notify_all()
+        for termination_callback in termination_callbacks:
+            termination_callback()
+
+    def unary_response_termination(self):
+        with self._condition:
+            while True:
+                if self._code is _CLIENT_INACTIVE:
+                    raise ValueError('Huh? Cancelled but wanting status?')
+                elif self._code is None:
+                    self._condition.wait()
+                else:
+                    if self._unary_response is None:
+                        if self._responses:
+                            self._unary_response = self._responses.pop(0)
+                    return (
+                        self._unary_response, self._trailing_metadata,
+                        self._code, self._details,)
+
+
+    def stream_response_termination(self):
+        with self._condition:
+            while True:
+                if self._code is _CLIENT_INACTIVE:
+                    raise ValueError('Huh? Cancelled but wanting status?')
+                elif self._code is None:
+                    self._condition.wait()
+                else:
+                    return self._trailing_metadata, self._code, self._details,
+
+    def expire(self):
+        with self._condition:
+            if self._code is None:
+                if self._initial_metadata is None:
+                    self._initial_metadata = _common.FUSSED_EMPTY_METADATA
+                self._trailing_metadata = _common.FUSSED_EMPTY_METADATA
+                self._code = grpc.StatusCode.DEADLINE_EXCEEDED
+                self._details = 'Took too much time!'
+                termination_callbacks = self._termination_callbacks
+                self._termination_callbacks = None
+                self._condition.notify_all()
+        for termination_callback in termination_callbacks:
+            termination_callback()
+
+    def set_expiration_future(self, expiration_future):
+        with self._condition:
+            self._expiration_future = expiration_future
+
+
+def handler_without_deadline(requests_closed):
+    return _Handler(requests_closed)
+
+
+def handler_with_deadline(requests_closed, time, deadline):
+    handler = _Handler(requests_closed)
+    expiration_future = time.call_at(handler.expire, deadline)
+    handler.set_expiration_future(expiration_future)
+    return handler
diff --git a/src/python/grpcio_testing/grpc_testing/_server/_rpc.py b/src/python/grpcio_testing/grpc_testing/_server/_rpc.py
new file mode 100644
index 0000000..f81876f
--- /dev/null
+++ b/src/python/grpcio_testing/grpc_testing/_server/_rpc.py
@@ -0,0 +1,153 @@
+# Copyright 2017 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.
+
+import logging
+import threading
+
+import grpc
+from grpc_testing import _common
+
+
+class Rpc(object):
+
+    def __init__(self, handler, invocation_metadata):
+        self._condition = threading.Condition()
+        self._handler = handler
+        self._invocation_metadata = invocation_metadata
+        self._initial_metadata_sent = False
+        self._pending_trailing_metadata = None
+        self._pending_code = None
+        self._pending_details = None
+        self._callbacks = []
+        self._active = True
+        self._rpc_errors = []
+
+    def _ensure_initial_metadata_sent(self):
+        if not self._initial_metadata_sent:
+            self._handler.send_initial_metadata(_common.FUSSED_EMPTY_METADATA)
+            self._initial_metadata_sent = True
+
+    def _call_back(self):
+        callbacks = tuple(self._callbacks)
+        self._callbacks = None
+
+        def call_back():
+            for callback in callbacks:
+                try:
+                    callback()
+                except Exception:  # pylint: disable=broad-except
+                    logging.exception('Exception calling server-side callback!')
+
+        callback_calling_thread = threading.Thread(target=call_back)
+        callback_calling_thread.start()
+
+    def _terminate(self, trailing_metadata, code, details):
+        if self._active:
+            self._active = False
+            self._handler.send_termination(trailing_metadata, code, details)
+            self._call_back()
+            self._condition.notify_all()
+
+    def _complete(self):
+        if self._pending_trailing_metadata is None:
+            trailing_metadata = _common.FUSSED_EMPTY_METADATA
+        else:
+            trailing_metadata = self._pending_trailing_metadata
+        if self._pending_code is None:
+            code = grpc.StatusCode.OK
+        else:
+            code = self._pending_code
+        details = '' if self._pending_details is None else self._pending_details
+        self._terminate(trailing_metadata, code, details)
+
+    def _abort(self, code, details):
+        self._terminate(_common.FUSSED_EMPTY_METADATA, code, details)
+
+    def add_rpc_error(self, rpc_error):
+        with self._condition:
+            self._rpc_errors.append(rpc_error)
+
+    def application_cancel(self):
+        with self._condition:
+            self._abort(
+                grpc.StatusCode.CANCELLED,
+                'Cancelled by server-side application!')
+
+    def application_exception_abort(self, exception):
+        with self._condition:
+            if exception not in self._rpc_errors:
+                logging.exception('Exception calling application!')
+                self._abort(
+                    grpc.StatusCode.UNKNOWN,
+                    'Exception calling application: {}'.format(exception))
+
+    def extrinsic_abort(self):
+        with self._condition:
+            if self._active:
+                self._active = False
+                self._call_back()
+                self._condition.notify_all()
+
+    def unary_response_complete(self, response):
+        with self._condition:
+            self._ensure_initial_metadata_sent()
+            self._handler.add_response(response)
+            self._complete()
+
+    def stream_response(self, response):
+        with self._condition:
+            self._ensure_initial_metadata_sent()
+            self._handler.add_response(response)
+
+    def stream_response_complete(self):
+        with self._condition:
+            self._ensure_initial_metadata_sent()
+            self._complete()
+
+    def send_initial_metadata(self, initial_metadata):
+        with self._condition:
+            if self._initial_metadata_sent:
+                return False
+            else:
+                self._handler.send_initial_metadata(initial_metadata)
+                self._initial_metadata_sent = True
+                return True
+
+    def is_active(self):
+        with self._condition:
+            return self._active
+
+    def add_callback(self, callback):
+        with self._condition:
+            if self._callbacks is None:
+                return False
+            else:
+                self._callbacks.append(callback)
+                return True
+
+    def invocation_metadata(self):
+        with self._condition:
+            return self._invocation_metadata
+
+    def set_trailing_metadata(self, trailing_metadata):
+        with self._condition:
+            self._pending_trailing_metadata = trailing_metadata
+
+    def set_code(self, code):
+        with self._condition:
+            self._pending_code = code
+
+    def set_details(self, details):
+        with self._condition:
+            self._pending_details = details
diff --git a/src/python/grpcio_testing/grpc_testing/_server/_server.py b/src/python/grpcio_testing/grpc_testing/_server/_server.py
new file mode 100644
index 0000000..66bcfc1
--- /dev/null
+++ b/src/python/grpcio_testing/grpc_testing/_server/_server.py
@@ -0,0 +1,149 @@
+# Copyright 2017 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.
+
+import threading
+
+import grpc_testing
+from grpc_testing import _common
+from grpc_testing._server import _handler
+from grpc_testing._server import _rpc
+from grpc_testing._server import _server_rpc
+from grpc_testing._server import _service
+from grpc_testing._server import _servicer_context
+
+
+def _implementation(descriptors_to_servicers, method_descriptor):
+    servicer = descriptors_to_servicers[method_descriptor.containing_service]
+    return getattr(servicer, method_descriptor.name)
+
+
+def _unary_unary_service(request):
+    def service(implementation, rpc, servicer_context):
+        _service.unary_unary(
+            implementation, rpc, request, servicer_context)
+    return service
+
+
+def _unary_stream_service(request):
+    def service(implementation, rpc, servicer_context):
+        _service.unary_stream(
+            implementation, rpc, request, servicer_context)
+    return service
+
+
+def _stream_unary_service(handler):
+    def service(implementation, rpc, servicer_context):
+        _service.stream_unary(implementation, rpc, handler, servicer_context)
+    return service
+
+
+def _stream_stream_service(handler):
+    def service(implementation, rpc, servicer_context):
+        _service.stream_stream(implementation, rpc, handler, servicer_context)
+    return service
+
+
+class _Serverish(_common.Serverish):
+
+    def __init__(self, descriptors_to_servicers, time):
+        self._descriptors_to_servicers = descriptors_to_servicers
+        self._time = time
+
+    def _invoke(
+            self, service_behavior, method_descriptor, handler,
+            invocation_metadata, deadline):
+        implementation = _implementation(
+            self._descriptors_to_servicers, method_descriptor)
+        rpc = _rpc.Rpc(handler, invocation_metadata)
+        if handler.add_termination_callback(rpc.extrinsic_abort):
+            servicer_context = _servicer_context.ServicerContext(
+                rpc, self._time, deadline)
+            service_thread = threading.Thread(
+                target=service_behavior,
+                args=(implementation, rpc, servicer_context,))
+            service_thread.start()
+
+    def invoke_unary_unary(
+            self, method_descriptor, handler, invocation_metadata, request,
+            deadline):
+        self._invoke(
+            _unary_unary_service(request), method_descriptor, handler,
+            invocation_metadata, deadline)
+
+    def invoke_unary_stream(
+            self, method_descriptor, handler, invocation_metadata, request,
+            deadline):
+        self._invoke(
+            _unary_stream_service(request), method_descriptor, handler,
+            invocation_metadata, deadline)
+
+    def invoke_stream_unary(
+            self, method_descriptor, handler, invocation_metadata, deadline):
+        self._invoke(
+            _stream_unary_service(handler), method_descriptor, handler,
+            invocation_metadata, deadline)
+
+    def invoke_stream_stream(
+            self, method_descriptor, handler, invocation_metadata, deadline):
+        self._invoke(
+            _stream_stream_service(handler), method_descriptor, handler,
+            invocation_metadata, deadline)
+
+
+def _deadline_and_handler(requests_closed, time, timeout):
+    if timeout is None:
+        return None, _handler.handler_without_deadline(requests_closed)
+    else:
+        deadline = time.time() + timeout
+        handler = _handler.handler_with_deadline(requests_closed, time, deadline)
+        return deadline, handler
+
+
+class _Server(grpc_testing.Server):
+
+    def __init__(self, serverish, time):
+        self._serverish = serverish
+        self._time = time
+
+    def invoke_unary_unary(
+            self, method_descriptor, invocation_metadata, request, timeout):
+        deadline, handler = _deadline_and_handler(True, self._time, timeout)
+        self._serverish.invoke_unary_unary(
+            method_descriptor, handler, invocation_metadata, request, deadline)
+        return _server_rpc.UnaryUnaryServerRpc(handler)
+
+    def invoke_unary_stream(
+            self, method_descriptor, invocation_metadata, request, timeout):
+        deadline, handler = _deadline_and_handler(True, self._time, timeout)
+        self._serverish.invoke_unary_stream(
+            method_descriptor, handler, invocation_metadata, request, deadline)
+        return _server_rpc.UnaryStreamServerRpc(handler)
+
+    def invoke_stream_unary(
+            self, method_descriptor, invocation_metadata, timeout):
+        deadline, handler = _deadline_and_handler(False, self._time, timeout)
+        self._serverish.invoke_stream_unary(
+            method_descriptor, handler, invocation_metadata, deadline)
+        return _server_rpc.StreamUnaryServerRpc(handler)
+
+    def invoke_stream_stream(
+            self, method_descriptor, invocation_metadata, timeout):
+        deadline, handler = _deadline_and_handler(False, self._time, timeout)
+        self._serverish.invoke_stream_stream(
+            method_descriptor, handler, invocation_metadata, deadline)
+        return _server_rpc.StreamStreamServerRpc(handler)
+
+
+def server_from_descriptor_to_servicers(descriptors_to_servicers, time):
+    return _Server(_Serverish(descriptors_to_servicers, time), time)
diff --git a/src/python/grpcio_testing/grpc_testing/_server/_server_rpc.py b/src/python/grpcio_testing/grpc_testing/_server/_server_rpc.py
new file mode 100644
index 0000000..30de8ff
--- /dev/null
+++ b/src/python/grpcio_testing/grpc_testing/_server/_server_rpc.py
@@ -0,0 +1,93 @@
+# Copyright 2017 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.
+
+import grpc_testing
+
+
+class UnaryUnaryServerRpc(grpc_testing.UnaryUnaryServerRpc):
+
+    def __init__(self, handler):
+        self._handler = handler
+
+    def initial_metadata(self):
+        return self._handler.initial_metadata()
+
+    def cancel(self):
+        self._handler.cancel()
+
+    def termination(self):
+        return self._handler.unary_response_termination()
+
+
+class UnaryStreamServerRpc(grpc_testing.UnaryStreamServerRpc):
+
+    def __init__(self, handler):
+        self._handler = handler
+
+    def initial_metadata(self):
+        return self._handler.initial_metadata()
+
+    def take_response(self):
+        return self._handler.take_response()
+
+    def cancel(self):
+        self._handler.cancel()
+
+    def termination(self):
+        return self._handler.stream_response_termination()
+
+
+class StreamUnaryServerRpc(grpc_testing.StreamUnaryServerRpc):
+
+    def __init__(self, handler):
+        self._handler = handler
+
+    def initial_metadata(self):
+        return self._handler.initial_metadata()
+
+    def send_request(self, request):
+        self._handler.add_request(request)
+
+    def requests_closed(self):
+        self._handler.requests_closed()
+
+    def cancel(self):
+        self._handler.cancel()
+
+    def termination(self):
+        return self._handler.unary_response_termination()
+
+
+class StreamStreamServerRpc(grpc_testing.StreamStreamServerRpc):
+
+    def __init__(self, handler):
+        self._handler = handler
+
+    def initial_metadata(self):
+        return self._handler.initial_metadata()
+
+    def send_request(self, request):
+        self._handler.add_request(request)
+
+    def requests_closed(self):
+        self._handler.requests_closed()
+
+    def take_response(self):
+        return self._handler.take_response()
+
+    def cancel(self):
+        self._handler.cancel()
+
+    def termination(self):
+        return self._handler.stream_response_termination()
diff --git a/src/python/grpcio_testing/grpc_testing/_server/_service.py b/src/python/grpcio_testing/grpc_testing/_server/_service.py
new file mode 100644
index 0000000..36b0a2f
--- /dev/null
+++ b/src/python/grpcio_testing/grpc_testing/_server/_service.py
@@ -0,0 +1,88 @@
+# Copyright 2017 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.
+
+import grpc
+
+
+class _RequestIterator(object):
+
+    def __init__(self, rpc, handler):
+        self._rpc = rpc
+        self._handler = handler
+
+    def _next(self):
+        read = self._handler.take_request()
+        if read.requests_closed:
+            raise StopIteration()
+        elif read.terminated:
+            rpc_error = grpc.RpcError()
+            self._rpc.add_rpc_error(rpc_error)
+            raise rpc_error
+        else:
+            return read.request
+
+    def __iter__(self):
+        return self
+
+    def __next__(self):
+        return self._next()
+
+    def next(self):
+        return self._next()
+
+
+def _unary_response(argument, implementation, rpc, servicer_context):
+    try:
+        response = implementation(argument, servicer_context)
+    except Exception as exception:  # pylint: disable=broad-except
+        rpc.application_exception_abort(exception)
+    else:
+        rpc.unary_response_complete(response)
+
+
+def _stream_response(argument, implementation, rpc, servicer_context):
+    try:
+        response_iterator = implementation(argument, servicer_context)
+    except Exception as exception:  # pylint: disable=broad-except
+        rpc.application_exception_abort(exception)
+    else:
+        while True:
+            try:
+                response = next(response_iterator)
+            except StopIteration:
+                rpc.stream_response_complete()
+                break
+            except Exception as exception:  # pylint: disable=broad-except
+                rpc.application_exception_abort(exception)
+                break
+            else:
+                rpc.stream_response(response)
+
+
+def unary_unary(implementation, rpc, request, servicer_context):
+    _unary_response(request, implementation, rpc, servicer_context)
+
+
+def unary_stream(implementation, rpc, request, servicer_context):
+    _stream_response(request, implementation, rpc, servicer_context)
+
+
+def stream_unary(implementation, rpc, handler, servicer_context):
+    _unary_response(
+        _RequestIterator(rpc, handler), implementation, rpc, servicer_context)
+
+
+def stream_stream(implementation, rpc, handler, servicer_context):
+    _stream_response(
+        _RequestIterator(rpc, handler), implementation, rpc, servicer_context)
diff --git a/src/python/grpcio_testing/grpc_testing/_server/_servicer_context.py b/src/python/grpcio_testing/grpc_testing/_server/_servicer_context.py
new file mode 100644
index 0000000..496689d
--- /dev/null
+++ b/src/python/grpcio_testing/grpc_testing/_server/_servicer_context.py
@@ -0,0 +1,74 @@
+# Copyright 2017 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.
+
+import grpc
+from grpc_testing import _common
+
+
+class ServicerContext(grpc.ServicerContext):
+
+    def __init__(self, rpc, time, deadline):
+        self._rpc = rpc
+        self._time = time
+        self._deadline = deadline
+
+    def is_active(self):
+        return self._rpc.is_active()
+
+    def time_remaining(self):
+        if self._rpc.is_active():
+            if self._deadline is None:
+                return None
+            else:
+                return max(0.0, self._deadline - self._time.time())
+        else:
+            return 0.0
+
+    def cancel(self):
+        self._rpc.application_cancel()
+
+    def add_callback(self, callback):
+        return self._rpc.add_callback(callback)
+
+    def invocation_metadata(self):
+        return self._rpc.invocation_metadata()
+
+    def peer(self):
+        raise NotImplementedError()
+
+    def peer_identities(self):
+        raise NotImplementedError()
+
+    def peer_identity_key(self):
+        raise NotImplementedError()
+
+    def auth_context(self):
+        raise NotImplementedError()
+
+    def send_initial_metadata(self, initial_metadata):
+        initial_metadata_sent = self._rpc.send_initial_metadata(
+            _common.fuss_with_metadata(initial_metadata))
+        if not initial_metadata_sent:
+            raise ValueError(
+                'ServicerContext.send_initial_metadata called too late!')
+
+    def set_trailing_metadata(self, trailing_metadata):
+        self._rpc.set_trailing_metadata(
+            _common.fuss_with_metadata(trailing_metadata))
+
+    def set_code(self, code):
+        self._rpc.set_code(code)
+
+    def set_details(self, details):
+        self._rpc.set_details(details)
diff --git a/src/python/grpcio_tests/tests/testing/_server_application.py b/src/python/grpcio_tests/tests/testing/_server_application.py
new file mode 100644
index 0000000..06f09c8
--- /dev/null
+++ b/src/python/grpcio_tests/tests/testing/_server_application.py
@@ -0,0 +1,66 @@
+# Copyright 2017 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 example gRPC Python-using server-side application."""
+
+import grpc
+
+# requests_pb2 is a semantic dependency of this module.
+from tests.testing import _application_common
+from tests.testing.proto import requests_pb2  # pylint: disable=unused-import
+from tests.testing.proto import services_pb2
+from tests.testing.proto import services_pb2_grpc
+
+
+class FirstServiceServicer(services_pb2_grpc.FirstServiceServicer):
+    """Services RPCs."""
+
+    def UnUn(self, request, context):
+        if _application_common.UNARY_UNARY_REQUEST == request:
+            return _application_common.UNARY_UNARY_RESPONSE
+        else:
+            context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
+            context.set_details('Something is wrong with your request!')
+            return services_pb2.Down()
+
+    def UnStre(self, request, context):
+        if _application_common.UNARY_STREAM_REQUEST != request:
+            context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
+            context.set_details('Something is wrong with your request!')
+        return
+        yield services_pb2.Strange()
+
+    def StreUn(self, request_iterator, context):
+        context.send_initial_metadata((
+            ('server_application_metadata_key', 'Hi there!',),))
+        for request in request_iterator:
+            if request != _application_common.STREAM_UNARY_REQUEST:
+                context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
+                context.set_details('Something is wrong with your request!')
+                return services_pb2.Strange()
+            elif not context.is_active():
+                return services_pb2.Strange()
+        else:
+            return _application_common.STREAM_UNARY_RESPONSE
+
+    def StreStre(self, request_iterator, context):
+        for request in request_iterator:
+            if request != _application_common.STREAM_STREAM_REQUEST:
+                context.set_code(grpc.StatusCode.INVALID_ARGUMENT)
+                context.set_details('Something is wrong with your request!')
+                return
+            elif not context.is_active():
+                return
+            else:
+                yield _application_common.STREAM_STREAM_RESPONSE
+                yield _application_common.STREAM_STREAM_RESPONSE
diff --git a/src/python/grpcio_tests/tests/testing/_server_test.py b/src/python/grpcio_tests/tests/testing/_server_test.py
new file mode 100644
index 0000000..7897bcc
--- /dev/null
+++ b/src/python/grpcio_tests/tests/testing/_server_test.py
@@ -0,0 +1,169 @@
+# Copyright 2017 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.
+
+import time
+import unittest
+
+import grpc
+import grpc_testing
+
+from tests.testing import _application_common
+from tests.testing import _application_testing_common
+from tests.testing import _server_application
+from tests.testing.proto import services_pb2
+
+
+# TODO(https://github.com/google/protobuf/issues/3452): Drop this skip.
+@unittest.skipIf(
+    services_pb2.DESCRIPTOR.services_by_name.get('FirstService') is None,
+    'Fix protobuf issue 3452!')
+class FirstServiceServicerTest(unittest.TestCase):
+
+    def setUp(self):
+        self._real_time = grpc_testing.strict_real_time()
+        self._fake_time = grpc_testing.strict_fake_time(time.time())
+        servicer = _server_application.FirstServiceServicer()
+        descriptors_to_servicers = {
+            _application_testing_common.FIRST_SERVICE: servicer
+        }
+        self._real_time_server = grpc_testing.server_from_dictionary(
+            descriptors_to_servicers, self._real_time)
+        self._fake_time_server = grpc_testing.server_from_dictionary(
+            descriptors_to_servicers, self._fake_time)
+
+    def test_successful_unary_unary(self):
+        rpc = self._real_time_server.invoke_unary_unary(
+            _application_testing_common.FIRST_SERVICE_UNUN, (),
+            _application_common.UNARY_UNARY_REQUEST, None)
+        initial_metadata = rpc.initial_metadata()
+        response, trailing_metadata, code, details = rpc.termination()
+
+        self.assertEqual(_application_common.UNARY_UNARY_RESPONSE, response)
+        self.assertIs(code, grpc.StatusCode.OK)
+
+    def test_successful_unary_stream(self):
+        rpc = self._real_time_server.invoke_unary_stream(
+            _application_testing_common.FIRST_SERVICE_UNSTRE, (),
+            _application_common.UNARY_STREAM_REQUEST, None)
+        initial_metadata = rpc.initial_metadata()
+        trailing_metadata, code, details = rpc.termination()
+
+        self.assertIs(code, grpc.StatusCode.OK)
+
+    def test_successful_stream_unary(self):
+        rpc = self._real_time_server.invoke_stream_unary(
+            _application_testing_common.FIRST_SERVICE_STREUN, (), None)
+        rpc.send_request(_application_common.STREAM_UNARY_REQUEST)
+        rpc.send_request(_application_common.STREAM_UNARY_REQUEST)
+        rpc.send_request(_application_common.STREAM_UNARY_REQUEST)
+        rpc.requests_closed()
+        initial_metadata = rpc.initial_metadata()
+        response, trailing_metadata, code, details = rpc.termination()
+
+        self.assertEqual(_application_common.STREAM_UNARY_RESPONSE, response)
+        self.assertIs(code, grpc.StatusCode.OK)
+
+    def test_successful_stream_stream(self):
+        rpc = self._real_time_server.invoke_stream_stream(
+            _application_testing_common.FIRST_SERVICE_STRESTRE, (), None)
+        rpc.send_request(_application_common.STREAM_STREAM_REQUEST)
+        initial_metadata = rpc.initial_metadata()
+        responses = [
+            rpc.take_response(),
+            rpc.take_response(),
+        ]
+        rpc.send_request(_application_common.STREAM_STREAM_REQUEST)
+        rpc.send_request(_application_common.STREAM_STREAM_REQUEST)
+        responses.extend([
+            rpc.take_response(),
+            rpc.take_response(),
+            rpc.take_response(),
+            rpc.take_response(),
+        ])
+        rpc.requests_closed()
+        trailing_metadata, code, details = rpc.termination()
+
+        for response in responses:
+            self.assertEqual(_application_common.STREAM_STREAM_RESPONSE,
+                             response)
+        self.assertIs(code, grpc.StatusCode.OK)
+
+    def test_server_rpc_idempotence(self):
+        rpc = self._real_time_server.invoke_unary_unary(
+            _application_testing_common.FIRST_SERVICE_UNUN, (),
+            _application_common.UNARY_UNARY_REQUEST, None)
+        first_initial_metadata = rpc.initial_metadata()
+        second_initial_metadata = rpc.initial_metadata()
+        third_initial_metadata = rpc.initial_metadata()
+        first_termination = rpc.termination()
+        second_termination = rpc.termination()
+        third_termination = rpc.termination()
+
+        for later_initial_metadata in (second_initial_metadata,
+                                       third_initial_metadata,):
+            self.assertEqual(first_initial_metadata, later_initial_metadata)
+        response = first_termination[0]
+        terminal_metadata = first_termination[1]
+        code = first_termination[2]
+        details = first_termination[3]
+        for later_termination in (second_termination, third_termination,):
+            self.assertEqual(response, later_termination[0])
+            self.assertEqual(terminal_metadata, later_termination[1])
+            self.assertIs(code, later_termination[2])
+            self.assertEqual(details, later_termination[3])
+        self.assertEqual(_application_common.UNARY_UNARY_RESPONSE, response)
+        self.assertIs(code, grpc.StatusCode.OK)
+
+    def test_misbehaving_client_unary_unary(self):
+        rpc = self._real_time_server.invoke_unary_unary(
+            _application_testing_common.FIRST_SERVICE_UNUN, (),
+            _application_common.ERRONEOUS_UNARY_UNARY_REQUEST, None)
+        initial_metadata = rpc.initial_metadata()
+        response, trailing_metadata, code, details = rpc.termination()
+
+        self.assertIsNot(code, grpc.StatusCode.OK)
+
+    def test_infinite_request_stream_real_time(self):
+        rpc = self._real_time_server.invoke_stream_unary(
+            _application_testing_common.FIRST_SERVICE_STREUN, (),
+            _application_common.INFINITE_REQUEST_STREAM_TIMEOUT)
+        rpc.send_request(_application_common.STREAM_UNARY_REQUEST)
+        rpc.send_request(_application_common.STREAM_UNARY_REQUEST)
+        rpc.send_request(_application_common.STREAM_UNARY_REQUEST)
+        initial_metadata = rpc.initial_metadata()
+        self._real_time.sleep_for(
+            _application_common.INFINITE_REQUEST_STREAM_TIMEOUT * 2)
+        rpc.send_request(_application_common.STREAM_UNARY_REQUEST)
+        response, trailing_metadata, code, details = rpc.termination()
+
+        self.assertIs(code, grpc.StatusCode.DEADLINE_EXCEEDED)
+
+    def test_infinite_request_stream_fake_time(self):
+        rpc = self._fake_time_server.invoke_stream_unary(
+            _application_testing_common.FIRST_SERVICE_STREUN, (),
+            _application_common.INFINITE_REQUEST_STREAM_TIMEOUT)
+        rpc.send_request(_application_common.STREAM_UNARY_REQUEST)
+        rpc.send_request(_application_common.STREAM_UNARY_REQUEST)
+        rpc.send_request(_application_common.STREAM_UNARY_REQUEST)
+        initial_metadata = rpc.initial_metadata()
+        self._fake_time.sleep_for(
+            _application_common.INFINITE_REQUEST_STREAM_TIMEOUT * 2)
+        rpc.send_request(_application_common.STREAM_UNARY_REQUEST)
+        response, trailing_metadata, code, details = rpc.termination()
+
+        self.assertIs(code, grpc.StatusCode.DEADLINE_EXCEEDED)
+
+
+if __name__ == '__main__':
+    unittest.main(verbosity=2)
diff --git a/src/python/grpcio_tests/tests/tests.json b/src/python/grpcio_tests/tests/tests.json
index c10719b..d61297b 100644
--- a/src/python/grpcio_tests/tests/tests.json
+++ b/src/python/grpcio_tests/tests/tests.json
@@ -10,6 +10,7 @@
   "protoc_plugin.beta_python_plugin_test.PythonPluginTest",
   "reflection._reflection_servicer_test.ReflectionServicerTest",
   "testing._client_test.ClientTest",
+  "testing._server_test.FirstServiceServicerTest",
   "testing._time_test.StrictFakeTimeTest",
   "testing._time_test.StrictRealTimeTest",
   "unit._api_test.AllTest",
diff --git a/src/ruby/.rubocop_todo.yml b/src/ruby/.rubocop_todo.yml
index 05db404..32b84b8 100644
--- a/src/ruby/.rubocop_todo.yml
+++ b/src/ruby/.rubocop_todo.yml
@@ -1,44 +1,569 @@
-# This configuration was generated by `rubocop --auto-gen-config`
-# on 2015-05-22 13:23:34 -0700 using RuboCop version 0.30.1.
+# This configuration was generated by
+# `rubocop --auto-gen-config`
+# on 2017-09-04 17:00:36 +0200 using RuboCop version 0.49.1.
 # The point is for the user to remove these configuration records
 # one by one as the offenses are removed from the code base.
 # Note that changes in the inspected code, or installation of new
 # versions of RuboCop, may require this file to be generated again.
 
-# Offense count: 30
-Metrics/AbcSize:
-  Max: 38
-
 # Offense count: 3
-# Configuration parameters: CountComments.
-Metrics/ClassLength:
-  Max: 200
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, IndentOneStep, IndentationWidth.
+# SupportedStyles: case, end
+Layout/CaseIndentation:
+  Exclude:
+    - 'tools/platform_check.rb'
 
-# Offense count: 35
-# Configuration parameters: CountComments.
-Metrics/MethodLength:
-  Max: 36
+# Offense count: 1
+# Cop supports --auto-correct.
+Layout/CommentIndentation:
+  Exclude:
+    - 'qps/client.rb'
 
-# Offense count: 7
-# Configuration parameters: CountKeywordArgs.
-Metrics/ParameterLists:
-  Max: 8
+# Offense count: 1
+# Cop supports --auto-correct.
+Layout/EmptyLineAfterMagicComment:
+  Exclude:
+    - 'tools/grpc-tools.gemspec'
 
-# Offense count: 9
-# Configuration parameters: AllowedVariables.
-Style/GlobalVars:
+# Offense count: 33
+# Cop supports --auto-correct.
+# Configuration parameters: AllowAdjacentOneLineDefs, NumberOfEmptyLines.
+Layout/EmptyLineBetweenDefs:
+  Exclude:
+    - 'qps/client.rb'
+    - 'qps/histogram.rb'
+    - 'qps/proxy-worker.rb'
+    - 'qps/server.rb'
+    - 'qps/worker.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+Layout/EmptyLines:
+  Exclude:
+    - 'qps/qps-common.rb'
+
+# Offense count: 8
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines
+Layout/EmptyLinesAroundClassBody:
+  Exclude:
+    - 'pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb'
+    - 'pb/grpc/testing/metrics_services_pb.rb'
+    - 'pb/src/proto/grpc/testing/test_services_pb.rb'
+    - 'qps/src/proto/grpc/testing/proxy-service_services_pb.rb'
+    - 'qps/src/proto/grpc/testing/services_services_pb.rb'
+
+# Offense count: 28
+# Cop supports --auto-correct.
+# Configuration parameters: AllowForAlignment, ForceEqualSignAlignment.
+Layout/ExtraSpacing:
   Enabled: false
 
 # Offense count: 1
-# Configuration parameters: EnforcedStyle, MinBodyLength, SupportedStyles.
-Style/Next:
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: normal, rails
+Layout/IndentationConsistency:
+  Exclude:
+    - 'pb/grpc/health/checker.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+# Configuration parameters: Width, IgnoredPatterns.
+Layout/IndentationWidth:
+  Exclude:
+    - 'pb/grpc/health/checker.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: symmetrical, new_line, same_line
+Layout/MultilineHashBraceLayout:
+  Exclude:
+    - 'spec/generic/active_call_spec.rb'
+
+# Offense count: 70
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: symmetrical, new_line, same_line
+Layout/MultilineMethodCallBraceLayout:
   Enabled: false
 
 # Offense count: 2
-# Configuration parameters: Methods.
-Style/SingleLineBlockParams:
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, IndentationWidth.
+# SupportedStyles: aligned, indented, indented_relative_to_receiver
+Layout/MultilineMethodCallIndentation:
+  Exclude:
+    - 'spec/generic/rpc_desc_spec.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: symmetrical, new_line, same_line
+Layout/MultilineMethodDefinitionBraceLayout:
+  Exclude:
+    - 'spec/generic/client_stub_spec.rb'
+
+# Offense count: 5
+# Cop supports --auto-correct.
+Layout/SpaceAfterColon:
+  Exclude:
+    - 'lib/grpc/generic/rpc_server.rb'
+
+# Offense count: 7
+# Cop supports --auto-correct.
+Layout/SpaceAfterComma:
+  Exclude:
+    - 'qps/client.rb'
+
+# Offense count: 27
+# Cop supports --auto-correct.
+# Configuration parameters: AllowForAlignment.
+Layout/SpaceAroundOperators:
+  Exclude:
+    - 'qps/client.rb'
+    - 'qps/histogram.rb'
+    - 'qps/proxy-worker.rb'
+    - 'qps/server.rb'
+    - 'spec/generic/active_call_spec.rb'
+    - 'spec/generic/rpc_server_spec.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces, SpaceBeforeBlockParameters.
+# SupportedStyles: space, no_space
+# SupportedStylesForEmptyBraces: space, no_space
+Layout/SpaceInsideBlockBraces:
+  Exclude:
+    - 'stress/stress_client.rb'
+
+# Offense count: 4
+# Cop supports --auto-correct.
+Layout/SpaceInsideBrackets:
+  Exclude:
+    - 'tools/bin/grpc_tools_ruby_protoc'
+    - 'tools/bin/grpc_tools_ruby_protoc_plugin'
+
+# Offense count: 2
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, EnforcedStyleForEmptyBraces, SupportedStylesForEmptyBraces.
+# SupportedStyles: space, no_space, compact
+# SupportedStylesForEmptyBraces: space, no_space
+Layout/SpaceInsideHashLiteralBraces:
+  Exclude:
+    - 'qps/server.rb'
+
+# Offense count: 6
+# Cop supports --auto-correct.
+Layout/SpaceInsidePercentLiteralDelimiters:
+  Exclude:
+    - 'spec/generic/client_stub_spec.rb'
+    - 'tools/grpc-tools.gemspec'
+
+# Offense count: 3
+# Cop supports --auto-correct.
+Layout/Tab:
+  Exclude:
+    - 'pb/grpc/health/checker.rb'
+    - 'qps/client.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+Layout/TrailingWhitespace:
+  Exclude:
+    - 'qps/worker.rb'
+
+# Offense count: 1
+Lint/IneffectiveAccessModifier:
+  Exclude:
+    - 'lib/grpc/generic/active_call.rb'
+
+# Offense count: 4
+# Cop supports --auto-correct.
+Lint/PercentStringArray:
+  Exclude:
+    - 'spec/client_server_spec.rb'
+    - 'spec/generic/active_call_spec.rb'
+    - 'spec/generic/client_stub_spec.rb'
+
+# Offense count: 4
+Lint/ScriptPermission:
+  Exclude:
+    - 'qps/client.rb'
+    - 'qps/histogram.rb'
+    - 'qps/qps-common.rb'
+    - 'qps/server.rb'
+
+# Offense count: 2
+# Cop supports --auto-correct.
+# Configuration parameters: IgnoreEmptyBlocks, AllowUnusedKeywordArguments.
+Lint/UnusedBlockArgument:
+  Exclude:
+    - 'qps/client.rb'
+
+# Offense count: 2
+# Cop supports --auto-correct.
+# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
+Lint/UnusedMethodArgument:
+  Exclude:
+    - 'qps/client.rb'
+
+# Offense count: 1
+# Configuration parameters: ContextCreatingMethods, MethodCreatingMethods.
+Lint/UselessAccessModifier:
+  Exclude:
+    - 'lib/grpc/logconfig.rb'
+
+# Offense count: 1
+Lint/UselessAssignment:
+  Exclude:
+    - 'qps/client.rb'
+
+# Offense count: 4
+Lint/Void:
+  Exclude:
+    - 'stress/metrics_server.rb'
+    - 'stress/stress_client.rb'
+
+# Offense count: 53
+Metrics/AbcSize:
+  Max: 57
+
+# Offense count: 81
+# Configuration parameters: CountComments, ExcludedMethods.
+Metrics/BlockLength:
+  Max: 715
+
+# Offense count: 82
+# Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, IgnoredPatterns.
+# URISchemes: http, https
+Metrics/LineLength:
+  Max: 141
+
+# Offense count: 82
+# Configuration parameters: CountComments.
+Metrics/MethodLength:
+  Max: 54
+
+# Offense count: 5
+# Configuration parameters: CountKeywordArgs.
+Metrics/ParameterLists:
+  Max: 7
+
+# Offense count: 1
+# Cop supports --auto-correct.
+Performance/RedundantBlockCall:
+  Exclude:
+    - 'spec/generic/client_stub_spec.rb'
+
+# Offense count: 5
+# Cop supports --auto-correct.
+# Configuration parameters: MaxKeyValuePairs.
+Performance/RedundantMerge:
+  Exclude:
+    - 'spec/generic/active_call_spec.rb'
+    - 'spec/generic/client_stub_spec.rb'
+
+# Offense count: 8
+# Cop supports --auto-correct.
+Performance/TimesMap:
+  Exclude:
+    - 'spec/channel_spec.rb'
+    - 'spec/client_server_spec.rb'
+    - 'spec/server_spec.rb'
+
+# Offense count: 7
+Style/AccessorMethodName:
+  Exclude:
+    - 'qps/server.rb'
+    - 'stress/metrics_server.rb'
+    - 'stress/stress_client.rb'
+
+# Offense count: 2
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: prefer_alias, prefer_alias_method
+Style/Alias:
+  Exclude:
+    - 'lib/grpc/generic/rpc_server.rb'
+    - 'lib/grpc/notifier.rb'
+
+# Offense count: 7
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, ProceduralMethods, FunctionalMethods, IgnoredMethods.
+# SupportedStyles: line_count_based, semantic, braces_for_chaining
+# ProceduralMethods: benchmark, bm, bmbm, create, each_with_object, measure, new, realtime, tap, with_object
+# FunctionalMethods: let, let!, subject, watch
+# IgnoredMethods: lambda, proc, it
+Style/BlockDelimiters:
+  Exclude:
+    - 'qps/client.rb'
+    - 'qps/proxy-worker.rb'
+    - 'qps/server.rb'
+    - 'qps/worker.rb'
+
+# Offense count: 2
+# Cop supports --auto-correct.
+Style/ClassMethods:
+  Exclude:
+    - 'tools/platform_check.rb'
+
+# Offense count: 2
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, SingleLineConditionsOnly, IncludeTernaryExpressions.
+# SupportedStyles: assign_to_condition, assign_inside_condition
+Style/ConditionalAssignment:
+  Exclude:
+    - 'lib/grpc/generic/rpc_server.rb'
+    - 'lib/grpc/generic/service.rb'
+
+# Offense count: 19
+Style/Documentation:
+  Exclude:
+    - 'spec/**/*'
+    - 'test/**/*'
+    - 'pb/grpc/testing/duplicate/echo_duplicate_services_pb.rb'
+    - 'pb/grpc/testing/metrics_services_pb.rb'
+    - 'pb/src/proto/grpc/testing/test_pb.rb'
+    - 'qps/client.rb'
+    - 'qps/histogram.rb'
+    - 'qps/proxy-worker.rb'
+    - 'qps/server.rb'
+    - 'qps/src/proto/grpc/testing/proxy-service_services_pb.rb'
+    - 'qps/src/proto/grpc/testing/services_pb.rb'
+    - 'qps/src/proto/grpc/testing/services_services_pb.rb'
+    - 'qps/worker.rb'
+    - 'stress/metrics_server.rb'
+    - 'stress/stress_client.rb'
+    - 'tools/platform_check.rb'
+
+# Offense count: 8
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: compact, expanded
+Style/EmptyMethod:
+  Exclude:
+    - 'bin/noproto_server.rb'
+    - 'lib/grpc/logconfig.rb'
+    - 'spec/generic/rpc_desc_spec.rb'
+
+# Offense count: 2
+# Configuration parameters: ExpectMatchingDefinition, Regex, IgnoreExecutableScripts, AllowedAcronyms.
+# AllowedAcronyms: CLI, DSL, ACL, API, ASCII, CPU, CSS, DNS, EOF, GUID, HTML, HTTP, HTTPS, ID, IP, JSON, LHS, QPS, RAM, RHS, RPC, SLA, SMTP, SQL, SSH, TCP, TLS, TTL, UDP, UI, UID, UUID, URI, URL, UTF8, VM, XML, XMPP, XSRF, XSS
+Style/FileName:
+  Exclude:
+    - 'qps/src/proto/grpc/testing/proxy-service_pb.rb'
+    - 'qps/src/proto/grpc/testing/proxy-service_services_pb.rb'
+
+# Offense count: 12
+# Configuration parameters: AllowedVariables.
+Style/GlobalVars:
+  Exclude:
+    - 'ext/grpc/extconf.rb'
+
+# Offense count: 3
+# Configuration parameters: MinBodyLength.
+Style/GuardClause:
+  Exclude:
+    - 'lib/grpc/generic/bidi_call.rb'
+    - 'lib/grpc/generic/rpc_server.rb'
+    - 'qps/client.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols.
+# SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys
+Style/HashSyntax:
+  Exclude:
+    - 'stress/metrics_server.rb'
+
+# Offense count: 1
+Style/IfInsideElse:
+  Exclude:
+    - 'lib/grpc/generic/rpc_desc.rb'
+
+# Offense count: 4
+# Cop supports --auto-correct.
+# Configuration parameters: MaxLineLength.
+Style/IfUnlessModifier:
+  Exclude:
+    - 'ext/grpc/extconf.rb'
+    - 'qps/histogram.rb'
+    - 'stress/stress_client.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+Style/MethodCallWithoutArgsParentheses:
+  Exclude:
+    - 'qps/client.rb'
+
+# Offense count: 3
+# Cop supports --auto-correct.
+Style/MultilineIfModifier:
+  Exclude:
+    - 'lib/grpc/generic/bidi_call.rb'
+    - 'lib/grpc/generic/client_stub.rb'
+    - 'spec/spec_helper.rb'
+
+# Offense count: 7
+# Cop supports --auto-correct.
+Style/MutableConstant:
+  Exclude:
+    - 'ext/grpc/extconf.rb'
+    - 'lib/grpc/version.rb'
+    - 'spec/compression_options_spec.rb'
+    - 'spec/generic/active_call_spec.rb'
+    - 'tools/version.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+Style/NegatedWhile:
+  Exclude:
+    - 'qps/client.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+# Configuration parameters: AutoCorrect, EnforcedStyle, SupportedStyles.
+# SupportedStyles: predicate, comparison
+Style/NumericPredicate:
+  Exclude:
+    - 'spec/**/*'
+    - 'ext/grpc/extconf.rb'
+
+# Offense count: 7
+# Cop supports --auto-correct.
+Style/ParallelAssignment:
+  Exclude:
+    - 'bin/math_server.rb'
+    - 'lib/grpc/generic/rpc_server.rb'
+    - 'spec/generic/client_stub_spec.rb'
+    - 'spec/generic/rpc_desc_spec.rb'
+    - 'spec/generic/rpc_server_pool_spec.rb'
+    - 'spec/generic/rpc_server_spec.rb'
+
+# Offense count: 8
+# Cop supports --auto-correct.
+# Configuration parameters: PreferredDelimiters.
+Style/PercentLiteralDelimiters:
+  Exclude:
+    - 'end2end/grpc_class_init_driver.rb'
+    - 'spec/client_server_spec.rb'
+    - 'spec/generic/active_call_spec.rb'
+    - 'spec/generic/client_stub_spec.rb'
+    - 'tools/grpc-tools.gemspec'
+
+# Offense count: 3
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: compact, exploded
+Style/RaiseArgs:
+  Exclude:
+    - 'stress/metrics_server.rb'
+
+# Offense count: 4
+# Cop supports --auto-correct.
+Style/RedundantParentheses:
+  Exclude:
+    - 'lib/grpc/generic/rpc_server.rb'
+    - 'qps/client.rb'
+    - 'qps/proxy-worker.rb'
+    - 'spec/generic/rpc_desc_spec.rb'
+
+# Offense count: 5
+# Cop supports --auto-correct.
+# Configuration parameters: AllowMultipleReturnValues.
+Style/RedundantReturn:
+  Exclude:
+    - 'end2end/grpc_class_init_client.rb'
+
+# Offense count: 77
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: only_raise, only_fail, semantic
+Style/SignalException:
   Enabled: false
 
+# Offense count: 2
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles.
+# SupportedStyles: use_perl_names, use_english_names
+Style/SpecialGlobalVars:
+  Exclude:
+    - 'ext/grpc/extconf.rb'
+    - 'stress/stress_client.rb'
+
+# Offense count: 189
+# Cop supports --auto-correct.
+# Configuration parameters: EnforcedStyle, SupportedStyles, ConsistentQuotesInMultiline.
+# SupportedStyles: single_quotes, double_quotes
+Style/StringLiterals:
+  Exclude:
+    - 'pb/grpc/testing/metrics_pb.rb'
+    - 'pb/src/proto/grpc/testing/empty_pb.rb'
+    - 'pb/src/proto/grpc/testing/messages_pb.rb'
+    - 'qps/proxy-worker.rb'
+    - 'qps/server.rb'
+    - 'qps/src/proto/grpc/testing/control_pb.rb'
+    - 'qps/src/proto/grpc/testing/messages_pb.rb'
+    - 'qps/src/proto/grpc/testing/payloads_pb.rb'
+    - 'qps/src/proto/grpc/testing/proxy-service_pb.rb'
+    - 'qps/src/proto/grpc/testing/stats_pb.rb'
+    - 'qps/worker.rb'
+
 # Offense count: 1
 Style/StructInheritance:
-  Enabled: false
+  Exclude:
+    - 'lib/grpc/generic/rpc_desc.rb'
+
+# Offense count: 10
+# Cop supports --auto-correct.
+# Configuration parameters: MinSize, SupportedStyles.
+# SupportedStyles: percent, brackets
+Style/SymbolArray:
+  EnforcedStyle: brackets
+
+# Offense count: 2
+# Cop supports --auto-correct.
+# Configuration parameters: IgnoredMethods.
+# IgnoredMethods: respond_to, define_method
+Style/SymbolProc:
+  Exclude:
+    - 'qps/client.rb'
+    - 'stress/stress_client.rb'
+
+# Offense count: 6
+# Cop supports --auto-correct.
+# Configuration parameters: AllowNamedUnderscoreVariables.
+Style/TrailingUnderscoreVariable:
+  Exclude:
+    - 'spec/channel_credentials_spec.rb'
+    - 'spec/server_credentials_spec.rb'
+
+# Offense count: 3
+# Cop supports --auto-correct.
+# Configuration parameters: ExactNameMatch, AllowPredicates, AllowDSLWriters, IgnoreClassMethods, Whitelist.
+# Whitelist: to_ary, to_a, to_c, to_enum, to_h, to_hash, to_i, to_int, to_io, to_open, to_path, to_proc, to_r, to_regexp, to_str, to_s, to_sym
+Style/TrivialAccessors:
+  Exclude:
+    - 'qps/histogram.rb'
+
+# Offense count: 3
+# Cop supports --auto-correct.
+Style/UnneededInterpolation:
+  Exclude:
+    - 'pb/grpc/health/checker.rb'
+
+# Offense count: 1
+# Cop supports --auto-correct.
+Style/YodaCondition:
+  Exclude:
+    - 'stress/stress_client.rb'
+
+# Offense count: 2
+# Cop supports --auto-correct.
+Style/ZeroLengthPredicate:
+  Exclude:
+    - 'lib/grpc/generic/rpc_server.rb'
diff --git a/src/ruby/end2end/grpc_class_init_client.rb b/src/ruby/end2end/grpc_class_init_client.rb
index b9e3d60..c35719a 100755
--- a/src/ruby/end2end/grpc_class_init_client.rb
+++ b/src/ruby/end2end/grpc_class_init_client.rb
@@ -41,7 +41,7 @@
   GC.enable
   construct_many(test_proc)
 
-  GC.start(full_mark: true, immediate_sweep: true)
+  GC.start
   construct_many(test_proc)
 end
 
diff --git a/templates/gRPC-Core.podspec.template b/templates/gRPC-Core.podspec.template
index 616f2cb..6077f80 100644
--- a/templates/gRPC-Core.podspec.template
+++ b/templates/gRPC-Core.podspec.template
@@ -171,6 +171,7 @@
                         'test/core/end2end/end2end_tests.{c,h}',
                         'test/core/end2end/end2end_test_utils.c',
                         'test/core/end2end/tests/*.{c,h}',
+                        'test/core/end2end/fixtures/*.h',
                         'test/core/end2end/data/*.{c,h}',
                         'test/core/util/debugger_macros.{c,h}',
                         'test/core/util/test_config.{c,h}',
diff --git a/templates/grpc.gemspec.template b/templates/grpc.gemspec.template
index 8b57993..e62e5b2 100644
--- a/templates/grpc.gemspec.template
+++ b/templates/grpc.gemspec.template
@@ -35,12 +35,12 @@
     s.add_development_dependency 'bundler',            '~> 1.9'
     s.add_development_dependency 'facter',             '~> 2.4'
     s.add_development_dependency 'logging',            '~> 2.0'
-    s.add_development_dependency 'simplecov',          '~> 0.9'
-    s.add_development_dependency 'rake',               '~> 10.4'
+    s.add_development_dependency 'simplecov',          '~> 0.14.1'
+    s.add_development_dependency 'rake',               '~> 12.0'
     s.add_development_dependency 'rake-compiler',      '~> 1.0'
     s.add_development_dependency 'rake-compiler-dock', '~> 0.5.1'
-    s.add_development_dependency 'rspec',              '~> 3.2'
-    s.add_development_dependency 'rubocop',            '~> 0.30.0'
+    s.add_development_dependency 'rspec',              '~> 3.6'
+    s.add_development_dependency 'rubocop',            '~> 0.49.1'
     s.add_development_dependency 'signet',             '~> 0.7.0'
 
     s.extensions = %w(src/ruby/ext/grpc/extconf.rb)
diff --git "a/templates/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec.template" "b/templates/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec.template"
index b822341..196c405 100644
--- "a/templates/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec.template"
+++ "b/templates/src/objective-c/\041ProtoCompiler-gRPCPlugin.podspec.template"
@@ -103,7 +103,7 @@
     s.preserve_paths = plugin
 
     # Restrict the protoc version to the one supported by this plugin.
-    s.dependency '!ProtoCompiler', '3.3.0'
+    s.dependency '!ProtoCompiler', '3.4.0'
     # For the Protobuf dependency not to complain:
     s.ios.deployment_target = '7.0'
     s.osx.deployment_target = '10.9'
diff --git a/test/core/tsi/BUILD b/test/core/tsi/BUILD
index 82e0e5f..0c5509d 100644
--- a/test/core/tsi/BUILD
+++ b/test/core/tsi/BUILD
@@ -18,13 +18,63 @@
 
 grpc_package(name = "test/core/tsi")
 
+grpc_cc_library(
+    name = "transport_security_test_lib",
+    srcs = ["transport_security_test_lib.c"],
+    hdrs = ["transport_security_test_lib.h"],
+    deps = [
+        "//:grpc",
+        "//:tsi",
+    ],
+)
+
+grpc_cc_test(
+    name = "fake_transport_security_test",
+    srcs = ["fake_transport_security_test.c"],
+    language = "C",
+    deps = [
+        ":transport_security_test_lib",
+        "//:grpc",
+        "//:gpr",
+        "//:tsi",
+        "//test/core/util:gpr_test_util",
+    ],
+)
+
+
+grpc_cc_test(
+    name = "ssl_transport_security_test",
+    srcs = ["ssl_transport_security_test.c"],
+    data = [
+        "//src/core/tsi/test_creds:badclient.key",
+        "//src/core/tsi/test_creds:badclient.pem",
+        "//src/core/tsi/test_creds:badserver.key",
+        "//src/core/tsi/test_creds:badserver.pem",
+        "//src/core/tsi/test_creds:ca.pem",
+        "//src/core/tsi/test_creds:client.key",
+        "//src/core/tsi/test_creds:client.pem",
+        "//src/core/tsi/test_creds:server0.key",
+        "//src/core/tsi/test_creds:server0.pem",
+        "//src/core/tsi/test_creds:server1.key",
+        "//src/core/tsi/test_creds:server1.pem",
+    ],
+    language = "C",
+    deps = [
+        ":transport_security_test_lib",
+        "//:grpc",
+        "//:gpr",
+        "//:tsi",
+        "//test/core/util:gpr_test_util",
+    ],
+)
+
 grpc_cc_test(
     name = "transport_security_test",
     srcs = ["transport_security_test.c"],
     language = "C",
     deps = [
-        "//:gpr",
         "//:grpc",
+        "//:gpr",
         "//test/core/util:gpr_test_util",
         "//test/core/util:grpc_test_util",
     ],
diff --git a/test/core/tsi/fake_transport_security_test.c b/test/core/tsi/fake_transport_security_test.c
new file mode 100644
index 0000000..11be880
--- /dev/null
+++ b/test/core/tsi/fake_transport_security_test.c
@@ -0,0 +1,148 @@
+/*
+ *
+ * Copyright 2017 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.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/tsi/fake_transport_security.h"
+#include "test/core/tsi/transport_security_test_lib.h"
+#include "test/core/util/test_config.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+
+typedef struct fake_tsi_test_fixture {
+  tsi_test_fixture base;
+} fake_tsi_test_fixture;
+
+static void fake_test_setup_handshakers(tsi_test_fixture *fixture) {
+  fixture->client_handshaker =
+      tsi_create_fake_handshaker(true /* is_client. */);
+  fixture->server_handshaker =
+      tsi_create_fake_handshaker(false /* is_client. */);
+}
+
+static void validate_handshaker_peers(tsi_handshaker_result *result) {
+  GPR_ASSERT(result != NULL);
+  tsi_peer peer;
+  GPR_ASSERT(tsi_handshaker_result_extract_peer(result, &peer) == TSI_OK);
+  const tsi_peer_property *property =
+      tsi_peer_get_property_by_name(&peer, TSI_CERTIFICATE_TYPE_PEER_PROPERTY);
+  GPR_ASSERT(property != NULL);
+  GPR_ASSERT(memcmp(property->value.data, TSI_FAKE_CERTIFICATE_TYPE,
+                    property->value.length) == 0);
+  tsi_peer_destruct(&peer);
+}
+
+static void fake_test_check_handshaker_peers(tsi_test_fixture *fixture) {
+  validate_handshaker_peers(fixture->client_result);
+  validate_handshaker_peers(fixture->server_result);
+}
+
+static void fake_test_destruct(tsi_test_fixture *fixture) {}
+
+static const struct tsi_test_fixture_vtable vtable = {
+    fake_test_setup_handshakers, fake_test_check_handshaker_peers,
+    fake_test_destruct};
+
+static tsi_test_fixture *fake_tsi_test_fixture_create() {
+  fake_tsi_test_fixture *fake_fixture = gpr_zalloc(sizeof(*fake_fixture));
+  tsi_test_fixture_init(&fake_fixture->base);
+  fake_fixture->base.vtable = &vtable;
+  return &fake_fixture->base;
+}
+
+void fake_tsi_test_do_handshake_tiny_handshake_buffer() {
+  tsi_test_fixture *fixture = fake_tsi_test_fixture_create();
+  fixture->handshake_buffer_size = TSI_TEST_TINY_HANDSHAKE_BUFFER_SIZE;
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void fake_tsi_test_do_handshake_small_handshake_buffer() {
+  tsi_test_fixture *fixture = fake_tsi_test_fixture_create();
+  fixture->handshake_buffer_size = TSI_TEST_SMALL_HANDSHAKE_BUFFER_SIZE;
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void fake_tsi_test_do_handshake() {
+  tsi_test_fixture *fixture = fake_tsi_test_fixture_create();
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void fake_tsi_test_do_round_trip_for_all_configs() {
+  unsigned int *bit_array =
+      gpr_zalloc(sizeof(unsigned int) * TSI_TEST_NUM_OF_ARGUMENTS);
+  const unsigned int mask = 1U << (TSI_TEST_NUM_OF_ARGUMENTS - 1);
+  for (unsigned int val = 0; val < TSI_TEST_NUM_OF_COMBINATIONS; val++) {
+    unsigned int v = val;
+    for (unsigned int ind = 0; ind < TSI_TEST_NUM_OF_ARGUMENTS; ind++) {
+      bit_array[ind] = (v & mask) ? 1 : 0;
+      v <<= 1;
+    }
+    tsi_test_fixture *fixture = fake_tsi_test_fixture_create();
+    fake_tsi_test_fixture *fake_fixture = (fake_tsi_test_fixture *)fixture;
+    tsi_test_frame_protector_config_destroy(fake_fixture->base.config);
+    fake_fixture->base.config = tsi_test_frame_protector_config_create(
+        bit_array[0], bit_array[1], bit_array[2], bit_array[3], bit_array[4],
+        bit_array[5], bit_array[6], bit_array[7]);
+    tsi_test_do_round_trip(&fake_fixture->base);
+    tsi_test_fixture_destroy(fixture);
+  }
+  gpr_free(bit_array);
+}
+
+void fake_tsi_test_do_round_trip_odd_buffer_size() {
+  const size_t odd_sizes[] = {1025, 2051, 4103, 8207, 16409};
+  const size_t size = sizeof(odd_sizes) / sizeof(size_t);
+  for (size_t ind1 = 0; ind1 < size; ind1++) {
+    for (size_t ind2 = 0; ind2 < size; ind2++) {
+      for (size_t ind3 = 0; ind3 < size; ind3++) {
+        for (size_t ind4 = 0; ind4 < size; ind4++) {
+          for (size_t ind5 = 0; ind5 < size; ind5++) {
+            tsi_test_fixture *fixture = fake_tsi_test_fixture_create();
+            fake_tsi_test_fixture *fake_fixture =
+                (fake_tsi_test_fixture *)fixture;
+            tsi_test_frame_protector_config_set_buffer_size(
+                fake_fixture->base.config, odd_sizes[ind1], odd_sizes[ind2],
+                odd_sizes[ind3], odd_sizes[ind4], odd_sizes[ind5]);
+            tsi_test_do_round_trip(&fake_fixture->base);
+            tsi_test_fixture_destroy(fixture);
+          }
+        }
+      }
+    }
+  }
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  grpc_init();
+  fake_tsi_test_do_handshake_tiny_handshake_buffer();
+  fake_tsi_test_do_handshake_small_handshake_buffer();
+  fake_tsi_test_do_handshake();
+  fake_tsi_test_do_round_trip_for_all_configs();
+  fake_tsi_test_do_round_trip_odd_buffer_size();
+  grpc_shutdown();
+  return 0;
+}
diff --git a/test/core/tsi/ssl_transport_security_test.c b/test/core/tsi/ssl_transport_security_test.c
new file mode 100644
index 0000000..364dfa1
--- /dev/null
+++ b/test/core/tsi/ssl_transport_security_test.c
@@ -0,0 +1,558 @@
+/*
+ *
+ * Copyright 2017 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.
+ *
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "src/core/lib/iomgr/load_file.h"
+#include "src/core/lib/security/transport/security_connector.h"
+#include "src/core/tsi/ssl_transport_security.h"
+#include "src/core/tsi/transport_security_adapter.h"
+#include "test/core/tsi/transport_security_test_lib.h"
+#include "test/core/util/test_config.h"
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include <grpc/support/string_util.h>
+
+#define SSL_TSI_TEST_ALPN1 "foo"
+#define SSL_TSI_TEST_ALPN2 "toto"
+#define SSL_TSI_TEST_ALPN3 "baz"
+#define SSL_TSI_TEST_ALPN_NUM 2
+#define SSL_TSI_TEST_SERVER_KEY_CERT_PAIRS_NUM 2
+#define SSL_TSI_TEST_BAD_SERVER_KEY_CERT_PAIRS_NUM 1
+#define SSL_TSI_TEST_CREDENTIALS_DIR "src/core/tsi/test_creds/"
+
+typedef enum AlpnMode {
+  NO_ALPN,
+  ALPN_CLIENT_NO_SERVER,
+  ALPN_SERVER_NO_CLIENT,
+  ALPN_CLIENT_SERVER_OK,
+  ALPN_CLIENT_SERVER_MISMATCH
+} AlpnMode;
+
+typedef struct ssl_alpn_lib {
+  AlpnMode alpn_mode;
+  char **server_alpn_protocols;
+  char **client_alpn_protocols;
+  uint16_t num_server_alpn_protocols;
+  uint16_t num_client_alpn_protocols;
+} ssl_alpn_lib;
+
+typedef struct ssl_key_cert_lib {
+  bool use_bad_server_cert;
+  bool use_bad_client_cert;
+  char *root_cert;
+  tsi_ssl_pem_key_cert_pair *server_pem_key_cert_pairs;
+  tsi_ssl_pem_key_cert_pair *bad_server_pem_key_cert_pairs;
+  tsi_ssl_pem_key_cert_pair client_pem_key_cert_pair;
+  tsi_ssl_pem_key_cert_pair bad_client_pem_key_cert_pair;
+  uint16_t server_num_key_cert_pairs;
+  uint16_t bad_server_num_key_cert_pairs;
+} ssl_key_cert_lib;
+
+typedef struct ssl_tsi_test_fixture {
+  tsi_test_fixture base;
+  ssl_key_cert_lib *key_cert_lib;
+  ssl_alpn_lib *alpn_lib;
+  bool force_client_auth;
+  char *server_name_indication;
+  tsi_ssl_server_handshaker_factory *server_handshaker_factory;
+  tsi_ssl_client_handshaker_factory *client_handshaker_factory;
+} ssl_tsi_test_fixture;
+
+static void ssl_test_setup_handshakers(tsi_test_fixture *fixture) {
+  ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+  GPR_ASSERT(ssl_fixture != NULL);
+  GPR_ASSERT(ssl_fixture->key_cert_lib != NULL);
+  GPR_ASSERT(ssl_fixture->alpn_lib != NULL);
+  ssl_key_cert_lib *key_cert_lib = ssl_fixture->key_cert_lib;
+  ssl_alpn_lib *alpn_lib = ssl_fixture->alpn_lib;
+  /* Create client handshaker factory. */
+  tsi_ssl_pem_key_cert_pair *client_key_cert_pair = NULL;
+  if (ssl_fixture->force_client_auth) {
+    client_key_cert_pair = key_cert_lib->use_bad_client_cert
+                               ? &key_cert_lib->bad_client_pem_key_cert_pair
+                               : &key_cert_lib->client_pem_key_cert_pair;
+  }
+  char **client_alpn_protocols = NULL;
+  uint16_t num_client_alpn_protocols = 0;
+  if (alpn_lib->alpn_mode == ALPN_CLIENT_NO_SERVER ||
+      alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_OK ||
+      alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_MISMATCH) {
+    client_alpn_protocols = alpn_lib->client_alpn_protocols;
+    num_client_alpn_protocols = alpn_lib->num_client_alpn_protocols;
+  }
+  GPR_ASSERT(tsi_create_ssl_client_handshaker_factory(
+                 client_key_cert_pair, key_cert_lib->root_cert, NULL,
+                 (const char **)client_alpn_protocols,
+                 num_client_alpn_protocols,
+                 &ssl_fixture->client_handshaker_factory) == TSI_OK);
+  /* Create server handshaker factory. */
+  char **server_alpn_protocols = NULL;
+  uint16_t num_server_alpn_protocols = 0;
+  if (alpn_lib->alpn_mode == ALPN_SERVER_NO_CLIENT ||
+      alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_OK ||
+      alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_MISMATCH) {
+    server_alpn_protocols = alpn_lib->server_alpn_protocols;
+    num_server_alpn_protocols = alpn_lib->num_server_alpn_protocols;
+    if (alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_MISMATCH) {
+      num_server_alpn_protocols--;
+    }
+  }
+  GPR_ASSERT(tsi_create_ssl_server_handshaker_factory(
+                 key_cert_lib->use_bad_server_cert
+                     ? key_cert_lib->bad_server_pem_key_cert_pairs
+                     : key_cert_lib->server_pem_key_cert_pairs,
+                 key_cert_lib->use_bad_server_cert
+                     ? key_cert_lib->bad_server_num_key_cert_pairs
+                     : key_cert_lib->server_num_key_cert_pairs,
+                 key_cert_lib->root_cert, ssl_fixture->force_client_auth, NULL,
+                 (const char **)server_alpn_protocols,
+                 num_server_alpn_protocols,
+                 &ssl_fixture->server_handshaker_factory) == TSI_OK);
+  /* Create server and client handshakers. */
+  tsi_handshaker *client_handshaker = NULL;
+  GPR_ASSERT(tsi_ssl_client_handshaker_factory_create_handshaker(
+                 ssl_fixture->client_handshaker_factory,
+                 ssl_fixture->server_name_indication,
+                 &client_handshaker) == TSI_OK);
+  ssl_fixture->base.client_handshaker =
+      tsi_create_adapter_handshaker(client_handshaker);
+  tsi_handshaker *server_handshaker = NULL;
+  GPR_ASSERT(tsi_ssl_server_handshaker_factory_create_handshaker(
+                 ssl_fixture->server_handshaker_factory, &server_handshaker) ==
+             TSI_OK);
+  ssl_fixture->base.server_handshaker =
+      tsi_create_adapter_handshaker(server_handshaker);
+}
+
+static void check_alpn(ssl_tsi_test_fixture *ssl_fixture,
+                       const tsi_peer *peer) {
+  GPR_ASSERT(ssl_fixture != NULL);
+  GPR_ASSERT(ssl_fixture->alpn_lib != NULL);
+  ssl_alpn_lib *alpn_lib = ssl_fixture->alpn_lib;
+  const tsi_peer_property *alpn_property =
+      tsi_peer_get_property_by_name(peer, TSI_SSL_ALPN_SELECTED_PROTOCOL);
+  if (alpn_lib->alpn_mode != ALPN_CLIENT_SERVER_OK) {
+    GPR_ASSERT(alpn_property == NULL);
+  } else {
+    GPR_ASSERT(alpn_property != NULL);
+    const char *expected_match = "baz";
+    GPR_ASSERT(memcmp(alpn_property->value.data, expected_match,
+                      alpn_property->value.length) == 0);
+  }
+}
+
+static const tsi_peer_property *
+check_basic_authenticated_peer_and_get_common_name(const tsi_peer *peer) {
+  const tsi_peer_property *cert_type_property =
+      tsi_peer_get_property_by_name(peer, TSI_CERTIFICATE_TYPE_PEER_PROPERTY);
+  GPR_ASSERT(cert_type_property != NULL);
+  GPR_ASSERT(memcmp(cert_type_property->value.data, TSI_X509_CERTIFICATE_TYPE,
+                    cert_type_property->value.length) == 0);
+  const tsi_peer_property *property = tsi_peer_get_property_by_name(
+      peer, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY);
+  GPR_ASSERT(property != NULL);
+  return property;
+}
+
+void check_server0_peer(tsi_peer *peer) {
+  const tsi_peer_property *property =
+      check_basic_authenticated_peer_and_get_common_name(peer);
+  const char *expected_match = "*.test.google.com.au";
+  GPR_ASSERT(memcmp(property->value.data, expected_match,
+                    property->value.length) == 0);
+  GPR_ASSERT(tsi_peer_get_property_by_name(
+                 peer, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) ==
+             NULL);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "foo.test.google.com.au") == 1);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "bar.test.google.com.au") == 1);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "bar.test.google.blah") == 0);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "foo.bar.test.google.com.au") ==
+             0);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "test.google.com.au") == 0);
+  tsi_peer_destruct(peer);
+}
+
+static bool check_subject_alt_name(tsi_peer *peer, const char *name) {
+  for (size_t i = 0; i < peer->property_count; i++) {
+    const tsi_peer_property *prop = &peer->properties[i];
+    if (strcmp(prop->name, TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY) ==
+        0) {
+      if (memcmp(prop->value.data, name, prop->value.length) == 0) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
+void check_server1_peer(tsi_peer *peer) {
+  const tsi_peer_property *property =
+      check_basic_authenticated_peer_and_get_common_name(peer);
+  const char *expected_match = "*.test.google.com";
+  GPR_ASSERT(memcmp(property->value.data, expected_match,
+                    property->value.length) == 0);
+  GPR_ASSERT(check_subject_alt_name(peer, "*.test.google.fr") == 1);
+  GPR_ASSERT(check_subject_alt_name(peer, "waterzooi.test.google.be") == 1);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "foo.test.google.fr") == 1);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "bar.test.google.fr") == 1);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "waterzooi.test.google.be") == 1);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "foo.test.youtube.com") == 1);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "bar.foo.test.google.com") == 0);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "test.google.fr") == 0);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "tartines.test.google.be") == 0);
+  GPR_ASSERT(tsi_ssl_peer_matches_name(peer, "tartines.youtube.com") == 0);
+  tsi_peer_destruct(peer);
+}
+
+static void check_client_peer(ssl_tsi_test_fixture *ssl_fixture,
+                              tsi_peer *peer) {
+  GPR_ASSERT(ssl_fixture != NULL);
+  GPR_ASSERT(ssl_fixture->alpn_lib != NULL);
+  ssl_alpn_lib *alpn_lib = ssl_fixture->alpn_lib;
+  if (!ssl_fixture->force_client_auth) {
+    GPR_ASSERT(peer->property_count ==
+               (alpn_lib->alpn_mode == ALPN_CLIENT_SERVER_OK ? 1 : 0));
+  } else {
+    const tsi_peer_property *property =
+        check_basic_authenticated_peer_and_get_common_name(peer);
+    const char *expected_match = "testclient";
+    GPR_ASSERT(memcmp(property->value.data, expected_match,
+                      property->value.length) == 0);
+  }
+  tsi_peer_destruct(peer);
+}
+
+static void ssl_test_check_handshaker_peers(tsi_test_fixture *fixture) {
+  ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+  GPR_ASSERT(ssl_fixture != NULL);
+  GPR_ASSERT(ssl_fixture->key_cert_lib != NULL);
+  ssl_key_cert_lib *key_cert_lib = ssl_fixture->key_cert_lib;
+  tsi_peer peer;
+  bool expect_success =
+      !(key_cert_lib->use_bad_server_cert ||
+        (key_cert_lib->use_bad_client_cert && ssl_fixture->force_client_auth));
+  if (expect_success) {
+    GPR_ASSERT(tsi_handshaker_result_extract_peer(
+                   ssl_fixture->base.client_result, &peer) == TSI_OK);
+    check_alpn(ssl_fixture, &peer);
+
+    if (ssl_fixture->server_name_indication != NULL) {
+      check_server1_peer(&peer);
+    } else {
+      check_server0_peer(&peer);
+    }
+  } else {
+    GPR_ASSERT(ssl_fixture->base.client_result == NULL);
+  }
+  if (expect_success) {
+    GPR_ASSERT(tsi_handshaker_result_extract_peer(
+                   ssl_fixture->base.server_result, &peer) == TSI_OK);
+    check_alpn(ssl_fixture, &peer);
+    check_client_peer(ssl_fixture, &peer);
+  } else {
+    GPR_ASSERT(ssl_fixture->base.server_result == NULL);
+  }
+}
+
+static void ssl_test_pem_key_cert_pair_destroy(tsi_ssl_pem_key_cert_pair kp) {
+  gpr_free((void *)kp.private_key);
+  gpr_free((void *)kp.cert_chain);
+}
+
+static void ssl_test_destruct(tsi_test_fixture *fixture) {
+  ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+  if (ssl_fixture == NULL) {
+    return;
+  }
+  /* Destroy ssl_alpn_lib. */
+  ssl_alpn_lib *alpn_lib = ssl_fixture->alpn_lib;
+  for (size_t i = 0; i < alpn_lib->num_server_alpn_protocols; i++) {
+    gpr_free(alpn_lib->server_alpn_protocols[i]);
+  }
+  gpr_free(alpn_lib->server_alpn_protocols);
+  for (size_t i = 0; i < alpn_lib->num_client_alpn_protocols; i++) {
+    gpr_free(alpn_lib->client_alpn_protocols[i]);
+  }
+  gpr_free(alpn_lib->client_alpn_protocols);
+  gpr_free(alpn_lib);
+  /* Destroy ssl_key_cert_lib. */
+  ssl_key_cert_lib *key_cert_lib = ssl_fixture->key_cert_lib;
+  for (size_t i = 0; i < key_cert_lib->server_num_key_cert_pairs; i++) {
+    ssl_test_pem_key_cert_pair_destroy(
+        key_cert_lib->server_pem_key_cert_pairs[i]);
+  }
+  gpr_free(key_cert_lib->server_pem_key_cert_pairs);
+  for (size_t i = 0; i < key_cert_lib->bad_server_num_key_cert_pairs; i++) {
+    ssl_test_pem_key_cert_pair_destroy(
+        key_cert_lib->bad_server_pem_key_cert_pairs[i]);
+  }
+  gpr_free(key_cert_lib->bad_server_pem_key_cert_pairs);
+  ssl_test_pem_key_cert_pair_destroy(key_cert_lib->client_pem_key_cert_pair);
+  ssl_test_pem_key_cert_pair_destroy(
+      key_cert_lib->bad_client_pem_key_cert_pair);
+  gpr_free(key_cert_lib->root_cert);
+  gpr_free(key_cert_lib);
+  /* Destroy others. */
+  tsi_ssl_server_handshaker_factory_destroy(
+      ssl_fixture->server_handshaker_factory);
+  tsi_ssl_client_handshaker_factory_destroy(
+      ssl_fixture->client_handshaker_factory);
+}
+
+static const struct tsi_test_fixture_vtable vtable = {
+    ssl_test_setup_handshakers, ssl_test_check_handshaker_peers,
+    ssl_test_destruct};
+
+static char *load_file(const char *dir_path, const char *file_name) {
+  char *file_path =
+      gpr_zalloc(sizeof(char) * (strlen(dir_path) + strlen(file_name) + 1));
+  memcpy(file_path, dir_path, strlen(dir_path));
+  memcpy(file_path + strlen(dir_path), file_name, strlen(file_name));
+  grpc_slice slice;
+  GPR_ASSERT(grpc_load_file(file_path, 1, &slice) == GRPC_ERROR_NONE);
+  char *data = grpc_slice_to_c_string(slice);
+  grpc_slice_unref(slice);
+  gpr_free(file_path);
+  return data;
+}
+
+static tsi_test_fixture *ssl_tsi_test_fixture_create() {
+  ssl_tsi_test_fixture *ssl_fixture = gpr_zalloc(sizeof(*ssl_fixture));
+  tsi_test_fixture_init(&ssl_fixture->base);
+  ssl_fixture->base.test_unused_bytes = false;
+  ssl_fixture->base.vtable = &vtable;
+  /* Create ssl_key_cert_lib. */
+  ssl_key_cert_lib *key_cert_lib = gpr_zalloc(sizeof(*key_cert_lib));
+  key_cert_lib->use_bad_server_cert = false;
+  key_cert_lib->use_bad_client_cert = false;
+  key_cert_lib->server_num_key_cert_pairs =
+      SSL_TSI_TEST_SERVER_KEY_CERT_PAIRS_NUM;
+  key_cert_lib->bad_server_num_key_cert_pairs =
+      SSL_TSI_TEST_BAD_SERVER_KEY_CERT_PAIRS_NUM;
+  key_cert_lib->server_pem_key_cert_pairs =
+      gpr_malloc(sizeof(tsi_ssl_pem_key_cert_pair) *
+                 key_cert_lib->server_num_key_cert_pairs);
+  key_cert_lib->bad_server_pem_key_cert_pairs =
+      gpr_malloc(sizeof(tsi_ssl_pem_key_cert_pair) *
+                 key_cert_lib->bad_server_num_key_cert_pairs);
+  key_cert_lib->server_pem_key_cert_pairs[0].private_key =
+      load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "server0.key");
+  key_cert_lib->server_pem_key_cert_pairs[0].cert_chain =
+      load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "server0.pem");
+  key_cert_lib->server_pem_key_cert_pairs[1].private_key =
+      load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "server1.key");
+  key_cert_lib->server_pem_key_cert_pairs[1].cert_chain =
+      load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "server1.pem");
+  key_cert_lib->bad_server_pem_key_cert_pairs[0].private_key =
+      load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "badserver.key");
+  key_cert_lib->bad_server_pem_key_cert_pairs[0].cert_chain =
+      load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "badserver.pem");
+  key_cert_lib->client_pem_key_cert_pair.private_key =
+      load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "client.key");
+  key_cert_lib->client_pem_key_cert_pair.cert_chain =
+      load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "client.pem");
+  key_cert_lib->bad_client_pem_key_cert_pair.private_key =
+      load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "badclient.key");
+  key_cert_lib->bad_client_pem_key_cert_pair.cert_chain =
+      load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "badclient.pem");
+  key_cert_lib->root_cert = load_file(SSL_TSI_TEST_CREDENTIALS_DIR, "ca.pem");
+  ssl_fixture->key_cert_lib = key_cert_lib;
+  /* Create ssl_alpn_lib. */
+  ssl_alpn_lib *alpn_lib = gpr_zalloc(sizeof(*alpn_lib));
+  alpn_lib->server_alpn_protocols =
+      gpr_zalloc(sizeof(char *) * SSL_TSI_TEST_ALPN_NUM);
+  alpn_lib->client_alpn_protocols =
+      gpr_zalloc(sizeof(char *) * SSL_TSI_TEST_ALPN_NUM);
+  alpn_lib->server_alpn_protocols[0] = gpr_strdup(SSL_TSI_TEST_ALPN1);
+  alpn_lib->server_alpn_protocols[1] = gpr_strdup(SSL_TSI_TEST_ALPN3);
+  alpn_lib->client_alpn_protocols[0] = gpr_strdup(SSL_TSI_TEST_ALPN2);
+  alpn_lib->client_alpn_protocols[1] = gpr_strdup(SSL_TSI_TEST_ALPN3);
+  alpn_lib->num_server_alpn_protocols = SSL_TSI_TEST_ALPN_NUM;
+  alpn_lib->num_client_alpn_protocols = SSL_TSI_TEST_ALPN_NUM;
+  alpn_lib->alpn_mode = NO_ALPN;
+  ssl_fixture->alpn_lib = alpn_lib;
+  ssl_fixture->base.vtable = &vtable;
+  ssl_fixture->server_name_indication = NULL;
+  ssl_fixture->force_client_auth = false;
+  return &ssl_fixture->base;
+}
+
+void ssl_tsi_test_do_handshake_tiny_handshake_buffer() {
+  tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+  fixture->handshake_buffer_size = TSI_TEST_TINY_HANDSHAKE_BUFFER_SIZE;
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void ssl_tsi_test_do_handshake_small_handshake_buffer() {
+  tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+  fixture->handshake_buffer_size = TSI_TEST_SMALL_HANDSHAKE_BUFFER_SIZE;
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void ssl_tsi_test_do_handshake() {
+  tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void ssl_tsi_test_do_handshake_with_client_authentication() {
+  tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+  ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+  ssl_fixture->force_client_auth = true;
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void ssl_tsi_test_do_handshake_with_server_name_indication_exact_domain() {
+  /* server1 cert contains "waterzooi.test.google.be" in SAN. */
+  tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+  ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+  ssl_fixture->server_name_indication = "waterzooi.test.google.be";
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void ssl_tsi_test_do_handshake_with_server_name_indication_wild_star_domain() {
+  /* server1 cert contains "*.test.google.fr" in SAN. */
+  tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+  ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+  ssl_fixture->server_name_indication = "juju.test.google.fr";
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void ssl_tsi_test_do_handshake_with_bad_server_cert() {
+  tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+  ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+  ssl_fixture->key_cert_lib->use_bad_server_cert = true;
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void ssl_tsi_test_do_handshake_with_bad_client_cert() {
+  tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+  ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+  ssl_fixture->key_cert_lib->use_bad_client_cert = true;
+  ssl_fixture->force_client_auth = true;
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void ssl_tsi_test_do_handshake_alpn_client_no_server() {
+  tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+  ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+  ssl_fixture->alpn_lib->alpn_mode = ALPN_CLIENT_NO_SERVER;
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void ssl_tsi_test_do_handshake_alpn_server_no_client() {
+  tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+  ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+  ssl_fixture->alpn_lib->alpn_mode = ALPN_SERVER_NO_CLIENT;
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void ssl_tsi_test_do_handshake_alpn_client_server_mismatch() {
+  tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+  ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+  ssl_fixture->alpn_lib->alpn_mode = ALPN_CLIENT_SERVER_MISMATCH;
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void ssl_tsi_test_do_handshake_alpn_client_server_ok() {
+  tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+  ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+  ssl_fixture->alpn_lib->alpn_mode = ALPN_CLIENT_SERVER_OK;
+  tsi_test_do_handshake(fixture);
+  tsi_test_fixture_destroy(fixture);
+}
+
+void ssl_tsi_test_do_round_trip_for_all_configs() {
+  unsigned int *bit_array =
+      gpr_zalloc(sizeof(unsigned int) * TSI_TEST_NUM_OF_ARGUMENTS);
+  const unsigned int mask = 1U << (TSI_TEST_NUM_OF_ARGUMENTS - 1);
+  for (unsigned int val = 0; val < TSI_TEST_NUM_OF_COMBINATIONS; val++) {
+    unsigned int v = val;
+    for (unsigned int ind = 0; ind < TSI_TEST_NUM_OF_ARGUMENTS; ind++) {
+      bit_array[ind] = (v & mask) ? 1 : 0;
+      v <<= 1;
+    }
+    tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+    ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+    tsi_test_frame_protector_config_destroy(ssl_fixture->base.config);
+    ssl_fixture->base.config = tsi_test_frame_protector_config_create(
+        bit_array[0], bit_array[1], bit_array[2], bit_array[3], bit_array[4],
+        bit_array[5], bit_array[6], bit_array[7]);
+    tsi_test_do_round_trip(&ssl_fixture->base);
+    tsi_test_fixture_destroy(fixture);
+  }
+  gpr_free(bit_array);
+}
+
+void ssl_tsi_test_do_round_trip_odd_buffer_size() {
+  const size_t odd_sizes[] = {1025, 2051, 4103, 8207, 16409};
+  const size_t size = sizeof(odd_sizes) / sizeof(size_t);
+  for (size_t ind1 = 0; ind1 < size; ind1++) {
+    for (size_t ind2 = 0; ind2 < size; ind2++) {
+      for (size_t ind3 = 0; ind3 < size; ind3++) {
+        for (size_t ind4 = 0; ind4 < size; ind4++) {
+          for (size_t ind5 = 0; ind5 < size; ind5++) {
+            tsi_test_fixture *fixture = ssl_tsi_test_fixture_create();
+            ssl_tsi_test_fixture *ssl_fixture = (ssl_tsi_test_fixture *)fixture;
+            tsi_test_frame_protector_config_set_buffer_size(
+                ssl_fixture->base.config, odd_sizes[ind1], odd_sizes[ind2],
+                odd_sizes[ind3], odd_sizes[ind4], odd_sizes[ind5]);
+            tsi_test_do_round_trip(&ssl_fixture->base);
+            tsi_test_fixture_destroy(fixture);
+          }
+        }
+      }
+    }
+  }
+}
+
+int main(int argc, char **argv) {
+  grpc_test_init(argc, argv);
+  grpc_init();
+  ssl_tsi_test_do_handshake_tiny_handshake_buffer();
+  ssl_tsi_test_do_handshake_small_handshake_buffer();
+  ssl_tsi_test_do_handshake();
+  ssl_tsi_test_do_handshake_with_client_authentication();
+  ssl_tsi_test_do_handshake_with_server_name_indication_exact_domain();
+  ssl_tsi_test_do_handshake_with_server_name_indication_wild_star_domain();
+  ssl_tsi_test_do_handshake_with_bad_server_cert();
+  ssl_tsi_test_do_handshake_with_bad_client_cert();
+  ssl_tsi_test_do_handshake_alpn_client_no_server();
+  ssl_tsi_test_do_handshake_alpn_server_no_client();
+  ssl_tsi_test_do_handshake_alpn_client_server_mismatch();
+  ssl_tsi_test_do_handshake_alpn_client_server_ok();
+  ssl_tsi_test_do_round_trip_for_all_configs();
+  ssl_tsi_test_do_round_trip_odd_buffer_size();
+  grpc_shutdown();
+  return 0;
+}
diff --git a/test/core/tsi/transport_security_test_lib.c b/test/core/tsi/transport_security_test_lib.c
new file mode 100644
index 0000000..7d66e11
--- /dev/null
+++ b/test/core/tsi/transport_security_test_lib.c
@@ -0,0 +1,550 @@
+/*
+ *
+ * Copyright 2017 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.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <grpc/grpc.h>
+#include <grpc/support/alloc.h>
+#include <grpc/support/log.h>
+#include "src/core/lib/security/transport/tsi_error.h"
+#include "test/core/tsi/transport_security_test_lib.h"
+
+typedef struct handshaker_args {
+  tsi_test_fixture *fixture;
+  unsigned char *handshake_buffer;
+  size_t handshake_buffer_size;
+  bool is_client;
+  bool transferred_data;
+  bool appended_unused_bytes;
+  grpc_error *error;
+} handshaker_args;
+
+static handshaker_args *handshaker_args_create(tsi_test_fixture *fixture,
+                                               bool is_client) {
+  GPR_ASSERT(fixture != NULL);
+  GPR_ASSERT(fixture->config != NULL);
+  handshaker_args *args = gpr_zalloc(sizeof(*args));
+  args->fixture = fixture;
+  args->handshake_buffer_size = fixture->handshake_buffer_size;
+  args->handshake_buffer = gpr_zalloc(args->handshake_buffer_size);
+  args->is_client = is_client;
+  args->error = GRPC_ERROR_NONE;
+  return args;
+}
+
+static void handshaker_args_destroy(handshaker_args *args) {
+  gpr_free(args->handshake_buffer);
+  GRPC_ERROR_UNREF(args->error);
+  gpr_free(args);
+}
+
+static void do_handshaker_next(handshaker_args *args);
+
+static void setup_handshakers(tsi_test_fixture *fixture) {
+  GPR_ASSERT(fixture != NULL);
+  GPR_ASSERT(fixture->vtable != NULL);
+  GPR_ASSERT(fixture->vtable->setup_handshakers != NULL);
+  fixture->vtable->setup_handshakers(fixture);
+}
+
+static void check_unused_bytes(tsi_test_fixture *fixture) {
+  tsi_handshaker_result *result_with_unused_bytes =
+      fixture->has_client_finished_first ? fixture->server_result
+                                         : fixture->client_result;
+  tsi_handshaker_result *result_without_unused_bytes =
+      fixture->has_client_finished_first ? fixture->client_result
+                                         : fixture->server_result;
+  const unsigned char *bytes = NULL;
+  size_t bytes_size = 0;
+  GPR_ASSERT(tsi_handshaker_result_get_unused_bytes(
+                 result_with_unused_bytes, &bytes, &bytes_size) == TSI_OK);
+  GPR_ASSERT(bytes_size == strlen(TSI_TEST_UNUSED_BYTES));
+  GPR_ASSERT(memcmp(bytes, TSI_TEST_UNUSED_BYTES, bytes_size) == 0);
+  GPR_ASSERT(tsi_handshaker_result_get_unused_bytes(
+                 result_without_unused_bytes, &bytes, &bytes_size) == TSI_OK);
+  GPR_ASSERT(bytes_size == 0);
+  GPR_ASSERT(bytes == NULL);
+}
+
+static void check_handshake_results(tsi_test_fixture *fixture) {
+  GPR_ASSERT(fixture != NULL);
+  GPR_ASSERT(fixture->vtable != NULL);
+  GPR_ASSERT(fixture->vtable->check_handshaker_peers != NULL);
+  /* Check handshaker peers. */
+  fixture->vtable->check_handshaker_peers(fixture);
+  /* Check unused bytes. */
+  if (fixture->test_unused_bytes) {
+    if (fixture->server_result != NULL && fixture->client_result != NULL) {
+      check_unused_bytes(fixture);
+    }
+    fixture->bytes_written_to_server_channel = 0;
+    fixture->bytes_written_to_client_channel = 0;
+    fixture->bytes_read_from_client_channel = 0;
+    fixture->bytes_read_from_server_channel = 0;
+  }
+}
+
+static void send_bytes_to_peer(tsi_test_fixture *fixture,
+                               const unsigned char *buf, size_t buf_size,
+                               bool is_client) {
+  GPR_ASSERT(fixture != NULL);
+  GPR_ASSERT(buf != NULL);
+  uint8_t *channel =
+      is_client ? fixture->server_channel : fixture->client_channel;
+  GPR_ASSERT(channel != NULL);
+  size_t *bytes_written = is_client ? &fixture->bytes_written_to_server_channel
+                                    : &fixture->bytes_written_to_client_channel;
+  GPR_ASSERT(bytes_written != NULL);
+  GPR_ASSERT(*bytes_written + buf_size <= TSI_TEST_DEFAULT_CHANNEL_SIZE);
+  /* Write data to channel. */
+  memcpy(channel + *bytes_written, buf, buf_size);
+  *bytes_written += buf_size;
+}
+
+static void maybe_append_unused_bytes(handshaker_args *args) {
+  GPR_ASSERT(args != NULL);
+  GPR_ASSERT(args->fixture != NULL);
+  tsi_test_fixture *fixture = args->fixture;
+  if (fixture->test_unused_bytes && !args->appended_unused_bytes) {
+    args->appended_unused_bytes = true;
+    send_bytes_to_peer(fixture, (const unsigned char *)TSI_TEST_UNUSED_BYTES,
+                       strlen(TSI_TEST_UNUSED_BYTES), args->is_client);
+    if (fixture->client_result != NULL && fixture->server_result == NULL) {
+      fixture->has_client_finished_first = true;
+    }
+  }
+}
+
+static void receive_bytes_from_peer(tsi_test_fixture *fixture,
+                                    unsigned char **buf, size_t *buf_size,
+                                    bool is_client) {
+  GPR_ASSERT(fixture != NULL);
+  GPR_ASSERT(*buf != NULL);
+  GPR_ASSERT(buf_size != NULL);
+  uint8_t *channel =
+      is_client ? fixture->client_channel : fixture->server_channel;
+  GPR_ASSERT(channel != NULL);
+  size_t *bytes_read = is_client ? &fixture->bytes_read_from_client_channel
+                                 : &fixture->bytes_read_from_server_channel;
+  size_t *bytes_written = is_client ? &fixture->bytes_written_to_client_channel
+                                    : &fixture->bytes_written_to_server_channel;
+  GPR_ASSERT(bytes_read != NULL);
+  GPR_ASSERT(bytes_written != NULL);
+  size_t to_read = *buf_size < *bytes_written - *bytes_read
+                       ? *buf_size
+                       : *bytes_written - *bytes_read;
+  /* Read data from channel. */
+  memcpy(*buf, channel + *bytes_read, to_read);
+  *buf_size = to_read;
+  *bytes_read += to_read;
+}
+
+static void send_message_to_peer(tsi_test_fixture *fixture,
+                                 tsi_frame_protector *protector,
+                                 bool is_client) {
+  /* Initialization. */
+  GPR_ASSERT(fixture != NULL);
+  GPR_ASSERT(fixture->config != NULL);
+  GPR_ASSERT(protector != NULL);
+  tsi_test_frame_protector_config *config = fixture->config;
+  unsigned char *protected_buffer = gpr_zalloc(config->protected_buffer_size);
+  size_t message_size =
+      is_client ? config->client_message_size : config->server_message_size;
+  uint8_t *message =
+      is_client ? config->client_message : config->server_message;
+  GPR_ASSERT(message != NULL);
+  const unsigned char *message_bytes = (const unsigned char *)message;
+  tsi_result result = TSI_OK;
+  /* Do protect and send protected data to peer. */
+  while (message_size > 0 && result == TSI_OK) {
+    size_t protected_buffer_size_to_send = config->protected_buffer_size;
+    size_t processed_message_size = message_size;
+    /* Do protect. */
+    result = tsi_frame_protector_protect(
+        protector, message_bytes, &processed_message_size, protected_buffer,
+        &protected_buffer_size_to_send);
+    GPR_ASSERT(result == TSI_OK);
+    /* Send protected data to peer. */
+    send_bytes_to_peer(fixture, protected_buffer, protected_buffer_size_to_send,
+                       is_client);
+    message_bytes += processed_message_size;
+    message_size -= processed_message_size;
+    /* Flush if we're done. */
+    if (message_size == 0) {
+      size_t still_pending_size;
+      do {
+        protected_buffer_size_to_send = config->protected_buffer_size;
+        result = tsi_frame_protector_protect_flush(
+            protector, protected_buffer, &protected_buffer_size_to_send,
+            &still_pending_size);
+        GPR_ASSERT(result == TSI_OK);
+        send_bytes_to_peer(fixture, protected_buffer,
+                           protected_buffer_size_to_send, is_client);
+      } while (still_pending_size > 0 && result == TSI_OK);
+      GPR_ASSERT(result == TSI_OK);
+    }
+  }
+  GPR_ASSERT(result == TSI_OK);
+  gpr_free(protected_buffer);
+}
+
+static void receive_message_from_peer(tsi_test_fixture *fixture,
+                                      tsi_frame_protector *protector,
+                                      unsigned char *message,
+                                      size_t *bytes_received, bool is_client) {
+  /* Initialization. */
+  GPR_ASSERT(fixture != NULL);
+  GPR_ASSERT(protector != NULL);
+  GPR_ASSERT(message != NULL);
+  GPR_ASSERT(bytes_received != NULL);
+  GPR_ASSERT(fixture->config != NULL);
+  tsi_test_frame_protector_config *config = fixture->config;
+  size_t read_offset = 0;
+  size_t message_offset = 0;
+  size_t read_from_peer_size = 0;
+  tsi_result result = TSI_OK;
+  bool done = false;
+  unsigned char *read_buffer = gpr_zalloc(config->read_buffer_allocated_size);
+  unsigned char *message_buffer =
+      gpr_zalloc(config->message_buffer_allocated_size);
+  /* Do unprotect on data received from peer. */
+  while (!done && result == TSI_OK) {
+    /* Receive data from peer. */
+    if (read_from_peer_size == 0) {
+      read_from_peer_size = config->read_buffer_allocated_size;
+      receive_bytes_from_peer(fixture, &read_buffer, &read_from_peer_size,
+                              is_client);
+      read_offset = 0;
+    }
+    if (read_from_peer_size == 0) {
+      done = true;
+    }
+    /* Do unprotect. */
+    size_t message_buffer_size;
+    do {
+      message_buffer_size = config->message_buffer_allocated_size;
+      size_t processed_size = read_from_peer_size;
+      result = tsi_frame_protector_unprotect(
+          protector, read_buffer + read_offset, &processed_size, message_buffer,
+          &message_buffer_size);
+      GPR_ASSERT(result == TSI_OK);
+      if (message_buffer_size > 0) {
+        memcpy(message + message_offset, message_buffer, message_buffer_size);
+        message_offset += message_buffer_size;
+      }
+      read_offset += processed_size;
+      read_from_peer_size -= processed_size;
+    } while ((read_from_peer_size > 0 || message_buffer_size > 0) &&
+             result == TSI_OK);
+    GPR_ASSERT(result == TSI_OK);
+  }
+  GPR_ASSERT(result == TSI_OK);
+  *bytes_received = message_offset;
+  gpr_free(read_buffer);
+  gpr_free(message_buffer);
+}
+
+grpc_error *on_handshake_next_done(tsi_result result, void *user_data,
+                                   const unsigned char *bytes_to_send,
+                                   size_t bytes_to_send_size,
+                                   tsi_handshaker_result *handshaker_result) {
+  handshaker_args *args = (handshaker_args *)user_data;
+  GPR_ASSERT(args != NULL);
+  GPR_ASSERT(args->fixture != NULL);
+  tsi_test_fixture *fixture = args->fixture;
+  grpc_error *error = GRPC_ERROR_NONE;
+  /* Read more data if we need to. */
+  if (result == TSI_INCOMPLETE_DATA) {
+    GPR_ASSERT(bytes_to_send_size == 0);
+    return error;
+  }
+  if (result != TSI_OK) {
+    return grpc_set_tsi_error_result(
+        GRPC_ERROR_CREATE_FROM_STATIC_STRING("Handshake failed"), result);
+  }
+  /* Update handshaker result. */
+  if (handshaker_result != NULL) {
+    tsi_handshaker_result **result_to_write =
+        args->is_client ? &fixture->client_result : &fixture->server_result;
+    GPR_ASSERT(*result_to_write == NULL);
+    *result_to_write = handshaker_result;
+  }
+  /* Send data to peer, if needed. */
+  if (bytes_to_send_size > 0) {
+    send_bytes_to_peer(args->fixture, bytes_to_send, bytes_to_send_size,
+                       args->is_client);
+    args->transferred_data = true;
+  }
+  if (handshaker_result != NULL) {
+    maybe_append_unused_bytes(args);
+  }
+  return error;
+}
+
+static void on_handshake_next_done_wrapper(
+    tsi_result result, void *user_data, const unsigned char *bytes_to_send,
+    size_t bytes_to_send_size, tsi_handshaker_result *handshaker_result) {
+  handshaker_args *args = (handshaker_args *)user_data;
+  args->error = on_handshake_next_done(result, user_data, bytes_to_send,
+                                       bytes_to_send_size, handshaker_result);
+}
+
+static bool is_handshake_finished_properly(handshaker_args *args) {
+  GPR_ASSERT(args != NULL);
+  GPR_ASSERT(args->fixture != NULL);
+  tsi_test_fixture *fixture = args->fixture;
+  if ((args->is_client && fixture->client_result != NULL) ||
+      (!args->is_client && fixture->server_result != NULL)) {
+    return true;
+  }
+  return false;
+}
+
+static void do_handshaker_next(handshaker_args *args) {
+  /* Initialization. */
+  GPR_ASSERT(args != NULL);
+  GPR_ASSERT(args->fixture != NULL);
+  tsi_test_fixture *fixture = args->fixture;
+  tsi_handshaker *handshaker =
+      args->is_client ? fixture->client_handshaker : fixture->server_handshaker;
+  if (is_handshake_finished_properly(args)) {
+    return;
+  }
+  tsi_handshaker_result *handshaker_result = NULL;
+  unsigned char *bytes_to_send = NULL;
+  size_t bytes_to_send_size = 0;
+  /* Receive data from peer, if available. */
+  size_t buf_size = args->handshake_buffer_size;
+  receive_bytes_from_peer(args->fixture, &args->handshake_buffer, &buf_size,
+                          args->is_client);
+  if (buf_size > 0) {
+    args->transferred_data = true;
+  }
+  /* Peform handshaker next. */
+  tsi_result result = tsi_handshaker_next(
+      handshaker, args->handshake_buffer, buf_size,
+      (const unsigned char **)&bytes_to_send, &bytes_to_send_size,
+      &handshaker_result, &on_handshake_next_done_wrapper, args);
+  if (result != TSI_ASYNC) {
+    args->error = on_handshake_next_done(result, args, bytes_to_send,
+                                         bytes_to_send_size, handshaker_result);
+  }
+}
+
+void tsi_test_do_handshake(tsi_test_fixture *fixture) {
+  /* Initializaiton. */
+  setup_handshakers(fixture);
+  handshaker_args *client_args =
+      handshaker_args_create(fixture, true /* is_client */);
+  handshaker_args *server_args =
+      handshaker_args_create(fixture, false /* is_client */);
+  /* Do handshake. */
+  do {
+    client_args->transferred_data = false;
+    server_args->transferred_data = false;
+    do_handshaker_next(client_args);
+    if (client_args->error != GRPC_ERROR_NONE) {
+      break;
+    }
+    do_handshaker_next(server_args);
+    if (server_args->error != GRPC_ERROR_NONE) {
+      break;
+    }
+    GPR_ASSERT(client_args->transferred_data || server_args->transferred_data);
+  } while (fixture->client_result == NULL || fixture->server_result == NULL);
+  /* Verify handshake results. */
+  check_handshake_results(fixture);
+  /* Cleanup. */
+  handshaker_args_destroy(client_args);
+  handshaker_args_destroy(server_args);
+}
+
+void tsi_test_do_round_trip(tsi_test_fixture *fixture) {
+  /* Initialization. */
+  GPR_ASSERT(fixture != NULL);
+  GPR_ASSERT(fixture->config != NULL);
+  tsi_test_frame_protector_config *config = fixture->config;
+  tsi_frame_protector *client_frame_protector = NULL;
+  tsi_frame_protector *server_frame_protector = NULL;
+  /* Perform handshake. */
+  tsi_test_do_handshake(fixture);
+  /* Create frame protectors.*/
+  size_t client_max_output_protected_frame_size =
+      config->client_max_output_protected_frame_size;
+  GPR_ASSERT(tsi_handshaker_result_create_frame_protector(
+                 fixture->client_result,
+                 client_max_output_protected_frame_size == 0
+                     ? NULL
+                     : &client_max_output_protected_frame_size,
+                 &client_frame_protector) == TSI_OK);
+  size_t server_max_output_protected_frame_size =
+      config->server_max_output_protected_frame_size;
+  GPR_ASSERT(tsi_handshaker_result_create_frame_protector(
+                 fixture->server_result,
+                 server_max_output_protected_frame_size == 0
+                     ? NULL
+                     : &server_max_output_protected_frame_size,
+                 &server_frame_protector) == TSI_OK);
+  /* Client sends a message to server. */
+  send_message_to_peer(fixture, client_frame_protector, true /* is_client */);
+  unsigned char *server_received_message =
+      gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE);
+  size_t server_received_message_size = 0;
+  receive_message_from_peer(
+      fixture, server_frame_protector, server_received_message,
+      &server_received_message_size, false /* is_client */);
+  GPR_ASSERT(config->client_message_size == server_received_message_size);
+  GPR_ASSERT(memcmp(config->client_message, server_received_message,
+                    server_received_message_size) == 0);
+  /* Server sends a message to client. */
+  send_message_to_peer(fixture, server_frame_protector, false /* is_client */);
+  unsigned char *client_received_message =
+      gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE);
+  size_t client_received_message_size = 0;
+  receive_message_from_peer(
+      fixture, client_frame_protector, client_received_message,
+      &client_received_message_size, true /* is_client */);
+  GPR_ASSERT(config->server_message_size == client_received_message_size);
+  GPR_ASSERT(memcmp(config->server_message, client_received_message,
+                    client_received_message_size) == 0);
+  /* Destroy server and client frame protectors. */
+  tsi_frame_protector_destroy(client_frame_protector);
+  tsi_frame_protector_destroy(server_frame_protector);
+  gpr_free(server_received_message);
+  gpr_free(client_received_message);
+}
+
+static unsigned char *generate_random_message(size_t size) {
+  size_t i;
+  unsigned char chars[] = "abcdefghijklmnopqrstuvwxyz1234567890";
+  unsigned char *output = gpr_zalloc(sizeof(unsigned char) * size);
+  for (i = 0; i < size - 1; ++i) {
+    output[i] = chars[rand() % (int)(sizeof(chars) - 1)];
+  }
+  return output;
+}
+
+tsi_test_frame_protector_config *tsi_test_frame_protector_config_create(
+    bool use_default_read_buffer_allocated_size,
+    bool use_default_message_buffer_allocated_size,
+    bool use_default_protected_buffer_size, bool use_default_client_message,
+    bool use_default_server_message,
+    bool use_default_client_max_output_protected_frame_size,
+    bool use_default_server_max_output_protected_frame_size,
+    bool use_default_handshake_buffer_size) {
+  tsi_test_frame_protector_config *config = gpr_zalloc(sizeof(*config));
+  /* Set the value for read_buffer_allocated_size. */
+  config->read_buffer_allocated_size =
+      use_default_read_buffer_allocated_size
+          ? TSI_TEST_DEFAULT_BUFFER_SIZE
+          : TSI_TEST_SMALL_READ_BUFFER_ALLOCATED_SIZE;
+  /* Set the value for message_buffer_allocated_size. */
+  config->message_buffer_allocated_size =
+      use_default_message_buffer_allocated_size
+          ? TSI_TEST_DEFAULT_BUFFER_SIZE
+          : TSI_TEST_SMALL_MESSAGE_BUFFER_ALLOCATED_SIZE;
+  /* Set the value for protected_buffer_size. */
+  config->protected_buffer_size = use_default_protected_buffer_size
+                                      ? TSI_TEST_DEFAULT_PROTECTED_BUFFER_SIZE
+                                      : TSI_TEST_SMALL_PROTECTED_BUFFER_SIZE;
+  /* Set the value for client message. */
+  config->client_message_size = use_default_client_message
+                                    ? TSI_TEST_BIG_MESSAGE_SIZE
+                                    : TSI_TEST_SMALL_MESSAGE_SIZE;
+  config->client_message =
+      use_default_client_message
+          ? generate_random_message(TSI_TEST_BIG_MESSAGE_SIZE)
+          : generate_random_message(TSI_TEST_SMALL_MESSAGE_SIZE);
+  /* Set the value for server message. */
+  config->server_message_size = use_default_server_message
+                                    ? TSI_TEST_BIG_MESSAGE_SIZE
+                                    : TSI_TEST_SMALL_MESSAGE_SIZE;
+  config->server_message =
+      use_default_server_message
+          ? generate_random_message(TSI_TEST_BIG_MESSAGE_SIZE)
+          : generate_random_message(TSI_TEST_SMALL_MESSAGE_SIZE);
+  /* Set the value for client max_output_protected_frame_size.
+     If it is 0, we pass NULL to tsi_handshaker_result_create_frame_protector(),
+     which then uses default protected frame size for it. */
+  config->client_max_output_protected_frame_size =
+      use_default_client_max_output_protected_frame_size
+          ? 0
+          : TSI_TEST_SMALL_CLIENT_MAX_OUTPUT_PROTECTED_FRAME_SIZE;
+  /* Set the value for server max_output_protected_frame_size.
+     If it is 0, we pass NULL to tsi_handshaker_result_create_frame_protector(),
+     which then uses default protected frame size for it. */
+  config->server_max_output_protected_frame_size =
+      use_default_server_max_output_protected_frame_size
+          ? 0
+          : TSI_TEST_SMALL_SERVER_MAX_OUTPUT_PROTECTED_FRAME_SIZE;
+  return config;
+}
+
+void tsi_test_frame_protector_config_set_buffer_size(
+    tsi_test_frame_protector_config *config, size_t read_buffer_allocated_size,
+    size_t message_buffer_allocated_size, size_t protected_buffer_size,
+    size_t client_max_output_protected_frame_size,
+    size_t server_max_output_protected_frame_size) {
+  GPR_ASSERT(config != NULL);
+  config->read_buffer_allocated_size = read_buffer_allocated_size;
+  config->message_buffer_allocated_size = message_buffer_allocated_size;
+  config->protected_buffer_size = protected_buffer_size;
+  config->client_max_output_protected_frame_size =
+      client_max_output_protected_frame_size;
+  config->server_max_output_protected_frame_size =
+      server_max_output_protected_frame_size;
+}
+
+void tsi_test_frame_protector_config_destroy(
+    tsi_test_frame_protector_config *config) {
+  GPR_ASSERT(config != NULL);
+  gpr_free(config->client_message);
+  gpr_free(config->server_message);
+  gpr_free(config);
+}
+
+void tsi_test_fixture_init(tsi_test_fixture *fixture) {
+  fixture->config = tsi_test_frame_protector_config_create(
+      true, true, true, true, true, true, true, true);
+  fixture->handshake_buffer_size = TSI_TEST_DEFAULT_BUFFER_SIZE;
+  fixture->client_channel = gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE);
+  fixture->server_channel = gpr_zalloc(TSI_TEST_DEFAULT_CHANNEL_SIZE);
+  fixture->bytes_written_to_client_channel = 0;
+  fixture->bytes_written_to_server_channel = 0;
+  fixture->bytes_read_from_client_channel = 0;
+  fixture->bytes_read_from_server_channel = 0;
+  fixture->test_unused_bytes = true;
+  fixture->has_client_finished_first = false;
+}
+
+void tsi_test_fixture_destroy(tsi_test_fixture *fixture) {
+  GPR_ASSERT(fixture != NULL);
+  tsi_test_frame_protector_config_destroy(fixture->config);
+  tsi_handshaker_destroy(fixture->client_handshaker);
+  tsi_handshaker_destroy(fixture->server_handshaker);
+  tsi_handshaker_result_destroy(fixture->client_result);
+  tsi_handshaker_result_destroy(fixture->server_result);
+  gpr_free(fixture->client_channel);
+  gpr_free(fixture->server_channel);
+  GPR_ASSERT(fixture->vtable != NULL);
+  GPR_ASSERT(fixture->vtable->destruct != NULL);
+  fixture->vtable->destruct(fixture);
+  gpr_free(fixture);
+}
diff --git a/test/core/tsi/transport_security_test_lib.h b/test/core/tsi/transport_security_test_lib.h
new file mode 100644
index 0000000..8ae2024
--- /dev/null
+++ b/test/core/tsi/transport_security_test_lib.h
@@ -0,0 +1,165 @@
+/*
+ *
+ * Copyright 2017 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.
+ *
+ */
+
+#ifndef GRPC_TEST_CORE_TSI_TRANSPORT_SECURITY_TEST_LIB_H_
+#define GRPC_TEST_CORE_TSI_TRANSPORT_SECURITY_TEST_LIB_H_
+
+#include "src/core/tsi/transport_security_interface.h"
+
+#define TSI_TEST_TINY_HANDSHAKE_BUFFER_SIZE 32
+#define TSI_TEST_SMALL_HANDSHAKE_BUFFER_SIZE 128
+#define TSI_TEST_SMALL_READ_BUFFER_ALLOCATED_SIZE 41
+#define TSI_TEST_SMALL_PROTECTED_BUFFER_SIZE 37
+#define TSI_TEST_SMALL_MESSAGE_BUFFER_ALLOCATED_SIZE 42
+#define TSI_TEST_SMALL_CLIENT_MAX_OUTPUT_PROTECTED_FRAME_SIZE 39
+#define TSI_TEST_SMALL_SERVER_MAX_OUTPUT_PROTECTED_FRAME_SIZE 43
+#define TSI_TEST_DEFAULT_BUFFER_SIZE 4096
+#define TSI_TEST_DEFAULT_PROTECTED_BUFFER_SIZE 16384
+#define TSI_TEST_DEFAULT_CHANNEL_SIZE 32768
+#define TSI_TEST_BIG_MESSAGE_SIZE 17000
+#define TSI_TEST_SMALL_MESSAGE_SIZE 10
+#define TSI_TEST_NUM_OF_ARGUMENTS 8
+#define TSI_TEST_NUM_OF_COMBINATIONS 256
+#define TSI_TEST_UNUSED_BYTES "HELLO GOOGLE"
+
+/* ---  tsi_test_fixture object ---
+  The tests for specific TSI implementations should create their own
+  custom "subclass" of this fixture, which wraps all information
+  that will be used to test correctness of TSI handshakes and frame
+  protect/unprotect operations with respect to TSI implementations. */
+typedef struct tsi_test_fixture tsi_test_fixture;
+
+/* ---  tsi_test_frame_protector_config object ---
+
+  This object is used to configure different parameters of TSI frame protector
+  APIs. */
+typedef struct tsi_test_frame_protector_config tsi_test_frame_protector_config;
+
+/* V-table for tsi_test_fixture operations that are implemented differently in
+   different TSI implementations. */
+typedef struct tsi_test_fixture_vtable {
+  void (*setup_handshakers)(tsi_test_fixture *fixture);
+  void (*check_handshaker_peers)(tsi_test_fixture *fixture);
+  void (*destruct)(tsi_test_fixture *fixture);
+} tranport_security_test_vtable;
+
+struct tsi_test_fixture {
+  const struct tsi_test_fixture_vtable *vtable;
+  /* client/server TSI handshaker used to perform TSI handshakes, and will get
+     instantiated during the call to setup_handshakers. */
+  tsi_handshaker *client_handshaker;
+  tsi_handshaker *server_handshaker;
+  /* client/server TSI handshaker results used to store the result of TSI
+     handshake. If the handshake fails, the result will store NULL upon
+     finishing the handshake. */
+  tsi_handshaker_result *client_result;
+  tsi_handshaker_result *server_result;
+  /* size of buffer used to store data received from the peer. */
+  size_t handshake_buffer_size;
+  /* simulated channels between client and server. If the server (client)
+     wants to send data to the client (server), he will write data to
+     client_channel (server_channel), which will be read by client (server). */
+  uint8_t *client_channel;
+  uint8_t *server_channel;
+  /* size of data written to the client/server channel. */
+  size_t bytes_written_to_client_channel;
+  size_t bytes_written_to_server_channel;
+  /* size of data read from the client/server channel */
+  size_t bytes_read_from_client_channel;
+  size_t bytes_read_from_server_channel;
+  /* tsi_test_frame_protector_config instance */
+  tsi_test_frame_protector_config *config;
+  /* a flag indicating if client has finished TSI handshake first (i.e., before
+     server).
+     The flag should be referred if and only if TSI handshake finishes
+     successfully. */
+  bool has_client_finished_first;
+  /* a flag indicating whether to test tsi_handshaker_result_get_unused_bytes()
+     for TSI implementation. This field is true by default, and false
+     for SSL TSI implementation due to grpc issue #12164
+     (https://github.com/grpc/grpc/issues/12164).
+  */
+  bool test_unused_bytes;
+};
+
+struct tsi_test_frame_protector_config {
+  /* size of buffer used to store protected frames to be unprotected. */
+  size_t read_buffer_allocated_size;
+  /* size of buffer used to store bytes resulted from unprotect operations. */
+  size_t message_buffer_allocated_size;
+  /* size of buffer used to store frames resulted from protect operations. */
+  size_t protected_buffer_size;
+  /* size of client/server maximum frame size. */
+  size_t client_max_output_protected_frame_size;
+  size_t server_max_output_protected_frame_size;
+  /* pointer that points to client/server message to be protected. */
+  uint8_t *client_message;
+  uint8_t *server_message;
+  /* size of client/server message. */
+  size_t client_message_size;
+  size_t server_message_size;
+};
+
+/* This method creates a tsi_test_frame_protector_config instance. Each
+   parameter of this function is a boolean value indicating whether to set the
+   corresponding parameter with a default value or not. If it's false, it will
+   be set with a specific value which is usually much smaller than the default.
+   Both values are defined with #define directive. */
+tsi_test_frame_protector_config *tsi_test_frame_protector_config_create(
+    bool use_default_read_buffer_allocated_size,
+    bool use_default_message_buffer_allocated_size,
+    bool use_default_protected_buffer_size, bool use_default_client_message,
+    bool use_default_server_message,
+    bool use_default_client_max_output_protected_frame_size,
+    bool use_default_server_max_output_protected_frame_size,
+    bool use_default_handshake_buffer_size);
+
+/* This method sets different buffer and frame sizes of a
+   tsi_test_frame_protector_config instance with user provided values. */
+void tsi_test_frame_protector_config_set_buffer_size(
+    tsi_test_frame_protector_config *config, size_t read_buffer_allocated_size,
+    size_t message_buffer_allocated_size, size_t protected_buffer_size,
+    size_t client_max_output_protected_frame_size,
+    size_t server_max_output_protected_frame_size);
+
+/* This method destroys a tsi_test_frame_protector_config instance. */
+void tsi_test_frame_protector_config_destroy(
+    tsi_test_frame_protector_config *config);
+
+/* This method initializes members of tsi_test_fixture instance.
+   Note that the struct instance should be allocated before making
+   this call. */
+void tsi_test_fixture_init(tsi_test_fixture *fixture);
+
+/* This method destroys a tsi_test_fixture instance. Note that the
+   fixture intance must be dynamically allocated and will be freed by
+   this function. */
+void tsi_test_fixture_destroy(tsi_test_fixture *fixture);
+
+/* This method performs a full TSI handshake between a client and a server.
+   Note that the test library will implement the new TSI handshaker API to
+   perform handshakes. */
+void tsi_test_do_handshake(tsi_test_fixture *fixture);
+
+/* This method performs a round trip test between the client and the server.
+   That is, the client sends a protected message to a server who receives the
+   message, and unprotects it. The same operation is triggered again with
+   the client and server switching its role. */
+void tsi_test_do_round_trip(tsi_test_fixture *fixture);
+
+#endif  // GRPC_TEST_CORE_TSI_TRANSPORT_SECURITY_TEST_LIB_H_
diff --git a/test/cpp/end2end/BUILD b/test/cpp/end2end/BUILD
index b8505c1..b29a13d 100644
--- a/test/cpp/end2end/BUILD
+++ b/test/cpp/end2end/BUILD
@@ -193,6 +193,7 @@
         "//test/cpp/util:test_util",
     ],
     external_deps = [
+        "gmock",
         "gtest",
     ],
 )
@@ -235,6 +236,7 @@
         "//test/cpp/util:test_util",
     ],
     external_deps = [
+        "gmock",
         "gtest",
     ],
 )
diff --git a/third_party/gtest.BUILD b/third_party/gtest.BUILD
index b70f2c5..f38bfd8 100644
--- a/third_party/gtest.BUILD
+++ b/third_party/gtest.BUILD
@@ -2,14 +2,38 @@
     name = "gtest",
     srcs = [
         "googletest/src/gtest-all.cc",
-	"googlemock/src/gmock-all.cc"
     ],
-    hdrs = glob(["googletest/include/**/*.h", "googletest/src/*.cc", "googletest/src/*.h", "googlemock/include/**/*.h", "googlemock/src/*.cc", "googlemock/src/*.h"]),
+    hdrs = glob([
+        "googletest/include/**/*.h",
+        "googletest/src/*.cc",
+        "googletest/src/*.h",
+    ]),
     includes = [
         "googletest",
         "googletest/include",
-	"googlemock",
-	"googlemock/include",
+    ],
+    linkstatic = 1,
+    visibility = [
+        "//visibility:public",
+    ],
+)
+
+cc_library(
+    name = "gmock",
+    srcs = [
+        "googlemock/src/gmock-all.cc"
+    ],
+    hdrs = glob([
+        "googlemock/include/**/*.h",
+        "googlemock/src/*.cc",
+        "googlemock/src/*.h"
+    ]),
+    includes = [
+        "googlemock",
+        "googlemock/include",
+    ],
+    deps = [
+        ":gtest",
     ],
     linkstatic = 1,
     visibility = [
diff --git a/tools/codegen/core/gen_hpack_tables.c b/tools/codegen/core/gen_hpack_tables.c
index 858ae20..73dfa9f 100644
--- a/tools/codegen/core/gen_hpack_tables.c
+++ b/tools/codegen/core/gen_hpack_tables.c
@@ -189,7 +189,10 @@
     return i;
   }
   GPR_ASSERT(nhuffstates != MAXHUFFSTATES);
-  i = nhuffstates++;
+ 
+  i = nhuffstates;
+  nhuffstates++;
+  
   huffstates[i].bitofs = bitofs;
   huffstates[i].syms = syms;
   huffstates[i].next = nibblelut_empty();
diff --git a/tools/codegen/core/gen_static_metadata.py b/tools/codegen/core/gen_static_metadata.py
index e56c627..6ee8a7c 100755
--- a/tools/codegen/core/gen_static_metadata.py
+++ b/tools/codegen/core/gen_static_metadata.py
@@ -132,29 +132,33 @@
     ('www-authenticate', ''),
 ]
 
+# Entries marked with is_default=True are ignored when counting
+# non-default initial metadata that prevents the chttp2 server from
+# sending a Trailers-Only response.
 METADATA_BATCH_CALLOUTS = [
-    ':path',
-    ':method',
-    ':status',
-    ':authority',
-    ':scheme',
-    'te',
-    'grpc-message',
-    'grpc-status',
-    'grpc-payload-bin',
-    'grpc-encoding',
-    'grpc-accept-encoding',
-    'grpc-server-stats-bin',
-    'grpc-tags-bin',
-    'grpc-trace-bin',
-    'content-type',
-    'content-encoding',
-    'accept-encoding',
-    'grpc-internal-encoding-request',
-    'grpc-internal-stream-encoding-request',
-    'user-agent',
-    'host',
-    'lb-token',
+    # (name, is_default)
+    (':path', True),
+    (':method', True),
+    (':status', True),
+    (':authority', True),
+    (':scheme', True),
+    ('te', True),
+    ('grpc-message', True),
+    ('grpc-status', True),
+    ('grpc-payload-bin', True),
+    ('grpc-encoding', True),
+    ('grpc-accept-encoding', True),
+    ('grpc-server-stats-bin', True),
+    ('grpc-tags-bin', True),
+    ('grpc-trace-bin', True),
+    ('content-type', True),
+    ('content-encoding', True),
+    ('accept-encoding', True),
+    ('grpc-internal-encoding-request', True),
+    ('grpc-internal-stream-encoding-request', True),
+    ('user-agent', True),
+    ('host', True),
+    ('lb-token', True),
 ]
 
 COMPRESSION_ALGORITHMS = [
@@ -235,7 +239,7 @@
 static_userdata = {}
 # put metadata batch callouts first, to make the check of if a static metadata
 # string is a callout trivial
-for elem in METADATA_BATCH_CALLOUTS:
+for elem, _ in METADATA_BATCH_CALLOUTS:
   if elem not in all_strs:
     all_strs.append(elem)
 for elem in CONFIG:
@@ -372,7 +376,7 @@
 
 
 # validate configuration
-for elem in METADATA_BATCH_CALLOUTS:
+for elem, _ in METADATA_BATCH_CALLOUTS:
   assert elem in all_strs
 
 print >> H, '#define GRPC_STATIC_MDSTR_COUNT %d' % len(all_strs)
@@ -540,7 +544,7 @@
 print >> C, '};'
 
 print >> H, 'typedef enum {'
-for elem in METADATA_BATCH_CALLOUTS:
+for elem, _ in METADATA_BATCH_CALLOUTS:
   print >> H, '  %s,' % mangle(elem, 'batch').upper()
 print >> H, '  GRPC_BATCH_CALLOUTS_COUNT'
 print >> H, '} grpc_metadata_batch_callouts_index;'
@@ -548,7 +552,7 @@
 print >> H, 'typedef union {'
 print >> H, '  struct grpc_linked_mdelem *array[GRPC_BATCH_CALLOUTS_COUNT];'
 print >> H, '  struct {'
-for elem in METADATA_BATCH_CALLOUTS:
+for elem, _ in METADATA_BATCH_CALLOUTS:
   print >> H, '  struct grpc_linked_mdelem *%s;' % mangle(elem, '').lower()
 print >> H, '  } named;'
 print >> H, '} grpc_metadata_batch_callouts;'
@@ -556,6 +560,14 @@
 print >> H, '#define GRPC_BATCH_INDEX_OF(slice) \\'
 print >> H, '  (GRPC_IS_STATIC_METADATA_STRING((slice)) ? (grpc_metadata_batch_callouts_index)GPR_CLAMP(GRPC_STATIC_METADATA_INDEX((slice)), 0, GRPC_BATCH_CALLOUTS_COUNT) : GRPC_BATCH_CALLOUTS_COUNT)'
 print >> H
+print >> H, ('extern bool grpc_static_callout_is_default['
+             'GRPC_BATCH_CALLOUTS_COUNT];')
+print >> H
+print >> C, 'bool grpc_static_callout_is_default[GRPC_BATCH_CALLOUTS_COUNT] = {'
+for elem, is_default in METADATA_BATCH_CALLOUTS:
+  print >> C, '  %s, // %s' % (str(is_default).lower(), elem)
+print >> C, '};'
+print >> C
 
 print >> H, 'extern const uint8_t grpc_static_accept_encoding_metadata[%d];' % (
     1 << len(COMPRESSION_ALGORITHMS))
diff --git a/tools/internal_ci/helper_scripts/prepare_build_macos_rc b/tools/internal_ci/helper_scripts/prepare_build_macos_rc
index 6da0e1e..f7fbec9 100644
--- a/tools/internal_ci/helper_scripts/prepare_build_macos_rc
+++ b/tools/internal_ci/helper_scripts/prepare_build_macos_rc
@@ -54,8 +54,7 @@
 brew install coreutils  # we need grealpath
 pip install virtualenv --user python
 pip install -U six tox setuptools --user python
-export PYTHONPATH=/Library/Python/2.7/site-packages
-source ~/.bashrc
+export PYTHONPATH=/Library/Python/3.4/site-packages
 
 # python 3.4
 wget -q https://www.python.org/ftp/python/3.4.4/python-3.4.4-macosx10.6.pkg
diff --git a/tools/internal_ci/macos/grpc_basictests_dbg.cfg b/tools/internal_ci/macos/grpc_basictests_dbg.cfg
index 53bda1f..f058f0c 100644
--- a/tools/internal_ci/macos/grpc_basictests_dbg.cfg
+++ b/tools/internal_ci/macos/grpc_basictests_dbg.cfg
@@ -27,5 +27,5 @@
 
 env_vars {
   key: "RUN_TESTS_FLAGS"
-  value: "-f basictests macos dbg --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
+  value: "-f basictests macos dbg --internal_ci -j 1 --inner_jobs 4"
 }
diff --git a/tools/internal_ci/macos/grpc_basictests_opt.cfg b/tools/internal_ci/macos/grpc_basictests_opt.cfg
index d359eb6..5048baa 100644
--- a/tools/internal_ci/macos/grpc_basictests_opt.cfg
+++ b/tools/internal_ci/macos/grpc_basictests_opt.cfg
@@ -27,5 +27,5 @@
 
 env_vars {
   key: "RUN_TESTS_FLAGS"
-  value: "-f basictests macos opt --internal_ci -j 1 --inner_jobs 4 --bq_result_table aggregate_results"
+  value: "-f basictests macos opt --internal_ci -j 1 --inner_jobs 4"
 }
diff --git a/tools/run_tests/generated/sources_and_headers.json b/tools/run_tests/generated/sources_and_headers.json
index 5ce25dc..fbd4738 100644
--- a/tools/run_tests/generated/sources_and_headers.json
+++ b/tools/run_tests/generated/sources_and_headers.json
@@ -541,6 +541,23 @@
       "gpr", 
       "gpr_test_util", 
       "grpc", 
+      "transport_security_test_lib"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "fake_transport_security_test", 
+    "src": [
+      "test/core/tsi/fake_transport_security_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
       "grpc_test_util"
     ], 
     "headers": [], 
@@ -2168,6 +2185,23 @@
       "gpr", 
       "gpr_test_util", 
       "grpc", 
+      "transport_security_test_lib"
+    ], 
+    "headers": [], 
+    "is_filegroup": false, 
+    "language": "c", 
+    "name": "ssl_transport_security_test", 
+    "src": [
+      "test/core/tsi/ssl_transport_security_test.c"
+    ], 
+    "third_party": false, 
+    "type": "target"
+  }, 
+  {
+    "deps": [
+      "gpr", 
+      "gpr_test_util", 
+      "grpc", 
       "grpc_test_util"
     ], 
     "headers": [], 
@@ -9108,6 +9142,23 @@
   }, 
   {
     "deps": [
+      "grpc"
+    ], 
+    "headers": [
+      "test/core/tsi/transport_security_test_lib.h"
+    ], 
+    "is_filegroup": true, 
+    "language": "c", 
+    "name": "transport_security_test_lib", 
+    "src": [
+      "test/core/tsi/transport_security_test_lib.c", 
+      "test/core/tsi/transport_security_test_lib.h"
+    ], 
+    "third_party": false, 
+    "type": "filegroup"
+  }, 
+  {
+    "deps": [
       "gpr", 
       "grpc_base", 
       "grpc_trace", 
diff --git a/tools/run_tests/generated/tests.json b/tools/run_tests/generated/tests.json
index 4368a57..7c90861 100644
--- a/tools/run_tests/generated/tests.json
+++ b/tools/run_tests/generated/tests.json
@@ -652,6 +652,26 @@
     ], 
     "cpu_cost": 1.0, 
     "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
+    "name": "fake_transport_security_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
     "exclude_iomgrs": [
       "uv"
     ], 
@@ -2278,6 +2298,26 @@
     "ci_platforms": [
       "linux", 
       "mac", 
+      "posix"
+    ], 
+    "cpu_cost": 1.0, 
+    "exclude_configs": [], 
+    "exclude_iomgrs": [], 
+    "flaky": false, 
+    "gtest": false, 
+    "language": "c", 
+    "name": "ssl_transport_security_test", 
+    "platforms": [
+      "linux", 
+      "mac", 
+      "posix"
+    ]
+  }, 
+  {
+    "args": [], 
+    "ci_platforms": [
+      "linux", 
+      "mac", 
       "posix", 
       "windows"
     ], 
diff --git a/tools/ubsan_suppressions.txt b/tools/ubsan_suppressions.txt
index 2dcfeea..6ccc306 100644
--- a/tools/ubsan_suppressions.txt
+++ b/tools/ubsan_suppressions.txt
@@ -5,6 +5,7 @@
 nonnull-attribute:ssl_copy_key_material
 alignment:CRYPTO_cbc128_encrypt
 alignment:CRYPTO_gcm128_encrypt
+alignment:poly1305_block_copy
 nonnull-attribute:google::protobuf::*
 alignment:google::protobuf::*
 nonnull-attribute:_tr_stored_block
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj
deleted file mode 100644
index 01eb473..0000000
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj
+++ /dev/null
@@ -1,298 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup Label="ProjectConfigurations">
-    <ProjectConfiguration Include="Debug|Win32">
-      <Configuration>Debug</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Debug|x64">
-      <Configuration>Debug</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|Win32">
-      <Configuration>Release</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|x64">
-      <Configuration>Release</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-  </ItemGroup>
-  <PropertyGroup Label="Globals">
-    <ProjectGuid>{47C2CB41-4E9F-58B6-F606-F6FAED5D00ED}</ProjectGuid>
-    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
-    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
-    <PlatformToolset>v100</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
-    <PlatformToolset>v110</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
-    <PlatformToolset>v120</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
-    <PlatformToolset>v140</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <UseDebugLibraries>false</UseDebugLibraries>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
-  <ImportGroup Label="ExtensionSettings">
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
-  </ImportGroup>
-  <PropertyGroup Label="UserMacros" />
-  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
-    <TargetName>end2end_nosec_tests</TargetName>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Release'">
-    <TargetName>end2end_nosec_tests</TargetName>
-  </PropertyGroup>
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>MaxSpeed</Optimization>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-    </Link>
-  </ItemDefinitionGroup>
-
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>MaxSpeed</Optimization>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-    </Link>
-  </ItemDefinitionGroup>
-
-  <ItemGroup>
-    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_test_helpers.h" />
-    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\end2end_tests.h" />
-  </ItemGroup>
-  <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_nosec_tests.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_test_utils.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\authority_not_supported.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_hostname.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_ping.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\binary_metadata.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_accept.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_client_done.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_invoke.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_round_trip.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_before_invoke.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_in_a_vacuum.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_with_status.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\compressed_payload.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\connectivity.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\default_host.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\disappearing_server.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\empty_batch.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_call_init_fails.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_causes_close.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_latency.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\graceful_server_shutdown.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\high_initial_seqno.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\hpack_size.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\idempotent_request.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\invoke_large_request.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\keepalive_timeout.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_age.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_idle.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_message_length.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\negative_deadline.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\network_status_change.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_logging.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_op.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\payload.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping_pong_streaming.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\proxy_auth.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\registered_call.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_flags.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_payload.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\resource_quota_server.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\server_finishes_request.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_calls.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_tags.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_cacheable_request.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_delayed_request.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_metadata.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_request.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_compressed_payload.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_payload.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_ping_pong_streaming.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\streaming_error_response.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\trailing_metadata.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\workaround_cronet_compression.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering_at_end.c">
-    </ClCompile>
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util_unsecure\grpc_test_util_unsecure.vcxproj">
-      <Project>{0A7E7F92-FDEA-40F1-A9EC-3BA484F98BBF}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_unsecure\grpc_unsecure.vcxproj">
-      <Project>{46CEDFFF-9692-456A-AA24-38B5D6BCF4C5}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
-      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
-      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
-    </ProjectReference>
-  </ItemGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-  <ImportGroup Label="ExtensionTargets">
-  </ImportGroup>
-  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
-    <PropertyGroup>
-      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
-    </PropertyGroup>
-  </Target>
-</Project>
-
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters b/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters
deleted file mode 100644
index fc066c5..0000000
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_nosec_tests/end2end_nosec_tests.vcxproj.filters
+++ /dev/null
@@ -1,209 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_nosec_tests.c">
-      <Filter>test\core\end2end</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_test_utils.c">
-      <Filter>test\core\end2end</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\authority_not_supported.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_hostname.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_ping.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\binary_metadata.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_accept.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_client_done.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_invoke.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_round_trip.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_before_invoke.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_in_a_vacuum.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_with_status.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\compressed_payload.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\connectivity.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\default_host.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\disappearing_server.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\empty_batch.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_call_init_fails.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_causes_close.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_latency.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\graceful_server_shutdown.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\high_initial_seqno.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\hpack_size.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\idempotent_request.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\invoke_large_request.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\keepalive_timeout.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_age.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_idle.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_message_length.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\negative_deadline.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\network_status_change.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_logging.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_op.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\payload.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping_pong_streaming.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\proxy_auth.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\registered_call.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_flags.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_payload.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\resource_quota_server.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\server_finishes_request.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_calls.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_tags.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_cacheable_request.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_delayed_request.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_metadata.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_request.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_compressed_payload.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_payload.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_ping_pong_streaming.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\streaming_error_response.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\trailing_metadata.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\workaround_cronet_compression.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering_at_end.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-  </ItemGroup>
-  <ItemGroup>
-    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_test_helpers.h">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\end2end_tests.h">
-      <Filter>test\core\end2end</Filter>
-    </ClInclude>
-  </ItemGroup>
-
-  <ItemGroup>
-    <Filter Include="test">
-      <UniqueIdentifier>{95f38e16-d400-0c90-0b5f-7670d392e71f}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core">
-      <UniqueIdentifier>{d58c2741-0940-80bc-ac98-9fe98ce69c36}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core\end2end">
-      <UniqueIdentifier>{f4baa425-3c27-3025-ecbc-a83c877ac226}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core\end2end\tests">
-      <UniqueIdentifier>{a828bc7b-f20c-0caa-e8ef-ca62e0bed371}</UniqueIdentifier>
-    </Filter>
-  </ItemGroup>
-</Project>
-
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj
deleted file mode 100644
index 6ea05a4..0000000
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj
+++ /dev/null
@@ -1,300 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup Label="ProjectConfigurations">
-    <ProjectConfiguration Include="Debug|Win32">
-      <Configuration>Debug</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Debug|x64">
-      <Configuration>Debug</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|Win32">
-      <Configuration>Release</Configuration>
-      <Platform>Win32</Platform>
-    </ProjectConfiguration>
-    <ProjectConfiguration Include="Release|x64">
-      <Configuration>Release</Configuration>
-      <Platform>x64</Platform>
-    </ProjectConfiguration>
-  </ItemGroup>
-  <PropertyGroup Label="Globals">
-    <ProjectGuid>{1F1F9084-2A93-B80E-364F-5754894AFAB4}</ProjectGuid>
-    <IgnoreWarnIntDirInTempDetected>true</IgnoreWarnIntDirInTempDetected>
-    <IntDir>$(SolutionDir)IntDir\$(MSBuildProjectName)\</IntDir>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '10.0'" Label="Configuration">
-    <PlatformToolset>v100</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '11.0'" Label="Configuration">
-    <PlatformToolset>v110</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '12.0'" Label="Configuration">
-    <PlatformToolset>v120</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(VisualStudioVersion)' == '14.0'" Label="Configuration">
-    <PlatformToolset>v140</PlatformToolset>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Debug'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <UseDebugLibraries>true</UseDebugLibraries>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Release'" Label="Configuration">
-    <ConfigurationType>StaticLibrary</ConfigurationType>
-    <UseDebugLibraries>false</UseDebugLibraries>
-    <WholeProgramOptimization>true</WholeProgramOptimization>
-    <CharacterSet>Unicode</CharacterSet>
-  </PropertyGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
-  <ImportGroup Label="ExtensionSettings">
-  </ImportGroup>
-  <ImportGroup Label="PropertySheets">
-    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
-    <Import Project="$(SolutionDir)\..\vsprojects\global.props" />
-    <Import Project="$(SolutionDir)\..\vsprojects\winsock.props" />
-  </ImportGroup>
-  <PropertyGroup Label="UserMacros" />
-  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
-    <TargetName>end2end_tests</TargetName>
-  </PropertyGroup>
-  <PropertyGroup Condition="'$(Configuration)'=='Release'">
-    <TargetName>end2end_tests</TargetName>
-  </PropertyGroup>
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>Disabled</Optimization>
-      <PreprocessorDefinitions>WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-    </Link>
-  </ItemDefinitionGroup>
-
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>MaxSpeed</Optimization>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-    </Link>
-  </ItemDefinitionGroup>
-
-    <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
-    <ClCompile>
-      <PrecompiledHeader>NotUsing</PrecompiledHeader>
-      <WarningLevel>Level3</WarningLevel>
-      <Optimization>MaxSpeed</Optimization>
-      <PreprocessorDefinitions>WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
-      <FunctionLevelLinking>true</FunctionLevelLinking>
-      <IntrinsicFunctions>true</IntrinsicFunctions>
-      <SDLCheck>true</SDLCheck>
-      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
-      <TreatWarningAsError>true</TreatWarningAsError>
-      <DebugInformationFormat Condition="$(Jenkins)">None</DebugInformationFormat>
-      <MinimalRebuild Condition="$(Jenkins)">false</MinimalRebuild>
-    </ClCompile>
-    <Link>
-      <SubSystem>Windows</SubSystem>
-      <GenerateDebugInformation Condition="!$(Jenkins)">true</GenerateDebugInformation>
-      <GenerateDebugInformation Condition="$(Jenkins)">false</GenerateDebugInformation>
-      <EnableCOMDATFolding>true</EnableCOMDATFolding>
-      <OptimizeReferences>true</OptimizeReferences>
-    </Link>
-  </ItemDefinitionGroup>
-
-  <ItemGroup>
-    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_test_helpers.h" />
-    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\end2end_tests.h" />
-  </ItemGroup>
-  <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_tests.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_test_utils.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\authority_not_supported.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_hostname.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_ping.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\binary_metadata.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\call_creds.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_accept.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_client_done.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_invoke.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_round_trip.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_before_invoke.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_in_a_vacuum.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_with_status.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\compressed_payload.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\connectivity.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\default_host.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\disappearing_server.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\empty_batch.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_call_init_fails.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_causes_close.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_latency.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\graceful_server_shutdown.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\high_initial_seqno.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\hpack_size.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\idempotent_request.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\invoke_large_request.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\keepalive_timeout.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_age.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_idle.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_message_length.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\negative_deadline.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\network_status_change.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_logging.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_op.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\payload.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping_pong_streaming.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\proxy_auth.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\registered_call.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_flags.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_payload.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\resource_quota_server.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\server_finishes_request.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_calls.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_tags.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_cacheable_request.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_delayed_request.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_metadata.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_request.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_compressed_payload.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_payload.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_ping_pong_streaming.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\streaming_error_response.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\trailing_metadata.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\workaround_cronet_compression.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering.c">
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering_at_end.c">
-    </ClCompile>
-  </ItemGroup>
-  <ItemGroup>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc_test_util\grpc_test_util.vcxproj">
-      <Project>{17BCAFC0-5FDC-4C94-AEB9-95F3E220614B}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\grpc\grpc.vcxproj">
-      <Project>{29D16885-7228-4C31-81ED-5F9187C7F2A9}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr_test_util\gpr_test_util.vcxproj">
-      <Project>{EAB0A629-17A9-44DB-B5FF-E91A721FE037}</Project>
-    </ProjectReference>
-    <ProjectReference Include="$(SolutionDir)\..\vsprojects\vcxproj\.\gpr\gpr.vcxproj">
-      <Project>{B23D3D1A-9438-4EDA-BEB6-9A0A03D17792}</Project>
-    </ProjectReference>
-  </ItemGroup>
-  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
-  <ImportGroup Label="ExtensionTargets">
-  </ImportGroup>
-  <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
-    <PropertyGroup>
-      <ErrorText>This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them.  For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}.</ErrorText>
-    </PropertyGroup>
-  </Target>
-</Project>
-
diff --git a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters b/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters
deleted file mode 100644
index 6ee37c1..0000000
--- a/vsprojects/vcxproj/test/end2end/tests/end2end_tests/end2end_tests.vcxproj.filters
+++ /dev/null
@@ -1,212 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
-  <ItemGroup>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_tests.c">
-      <Filter>test\core\end2end</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\end2end_test_utils.c">
-      <Filter>test\core\end2end</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\authority_not_supported.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_hostname.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\bad_ping.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\binary_metadata.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\call_creds.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_accept.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_client_done.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_invoke.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_after_round_trip.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_before_invoke.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_in_a_vacuum.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_with_status.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\compressed_payload.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\connectivity.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\default_host.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\disappearing_server.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\empty_batch.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_call_init_fails.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_causes_close.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\filter_latency.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\graceful_server_shutdown.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\high_initial_seqno.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\hpack_size.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\idempotent_request.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\invoke_large_request.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\keepalive_timeout.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\large_metadata.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\load_reporting_hook.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_concurrent_streams.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_age.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_connection_idle.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\max_message_length.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\negative_deadline.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\network_status_change.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_logging.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\no_op.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\payload.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\ping_pong_streaming.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\proxy_auth.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\registered_call.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_flags.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\request_with_payload.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\resource_quota_server.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\server_finishes_request.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_calls.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\shutdown_finishes_tags.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_cacheable_request.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_delayed_request.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_metadata.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\simple_request.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_compressed_payload.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_payload.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\stream_compression_ping_pong_streaming.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\streaming_error_response.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\trailing_metadata.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\workaround_cronet_compression.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-    <ClCompile Include="$(SolutionDir)\..\test\core\end2end\tests\write_buffering_at_end.c">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClCompile>
-  </ItemGroup>
-  <ItemGroup>
-    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\tests\cancel_test_helpers.h">
-      <Filter>test\core\end2end\tests</Filter>
-    </ClInclude>
-    <ClInclude Include="$(SolutionDir)\..\test\core\end2end\end2end_tests.h">
-      <Filter>test\core\end2end</Filter>
-    </ClInclude>
-  </ItemGroup>
-
-  <ItemGroup>
-    <Filter Include="test">
-      <UniqueIdentifier>{a8b0b6cf-6810-ce42-aabe-361c2a8fad86}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core">
-      <UniqueIdentifier>{8f9b8c49-9e5d-f954-5bae-c5cfc1e4494d}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core\end2end">
-      <UniqueIdentifier>{4b2ad304-b30c-df58-bbcd-352f6bee54bd}</UniqueIdentifier>
-    </Filter>
-    <Filter Include="test\core\end2end\tests">
-      <UniqueIdentifier>{594b8114-e3bd-e69b-3213-cc80cb3e8662}</UniqueIdentifier>
-    </Filter>
-  </ItemGroup>
-</Project>
-