Add CFStream tests
diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamClientTests.mm b/test/core/iomgr/ios/CFStreamTests/CFStreamClientTests.mm
new file mode 100644
index 0000000..414b6c7
--- /dev/null
+++ b/test/core/iomgr/ios/CFStreamTests/CFStreamClientTests.mm
@@ -0,0 +1,201 @@
+/*
+ *
+ * Copyright 2018 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 <XCTest/XCTest.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_CFSTREAM
+
+#include <netinet/in.h>
+
+#include <grpc/impl/codegen/sync.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "test/core/util/test_config.h"
+
+// static int g_connections_complete = 0;
+static gpr_mu g_mu;
+static int g_connections_complete = 0;
+static grpc_endpoint* g_connecting = nullptr;
+
+static void finish_connection() {
+  gpr_mu_lock(&g_mu);
+  g_connections_complete++;
+  gpr_mu_unlock(&g_mu);
+}
+
+static void must_succeed(void* arg, grpc_error* error) {
+  GPR_ASSERT(g_connecting != nullptr);
+  GPR_ASSERT(error == GRPC_ERROR_NONE);
+  grpc_endpoint_shutdown(g_connecting, GRPC_ERROR_CREATE_FROM_STATIC_STRING("must_succeed called"));
+  grpc_endpoint_destroy(g_connecting);
+  g_connecting = nullptr;
+  finish_connection();
+}
+
+static void must_fail(void* arg, grpc_error* error) {
+  GPR_ASSERT(g_connecting == nullptr);
+  GPR_ASSERT(error != GRPC_ERROR_NONE);
+  const char* error_str = grpc_error_string(error);
+  NSLog(@"%s", error_str);
+  finish_connection();
+}
+
+@interface CFStreamClientTests : XCTestCase
+
+@end
+
+@implementation CFStreamClientTests
+
++ (void)setUp {
+  grpc_init();
+  gpr_mu_init(&g_mu);
+}
+
++ (void)tearDown {
+  grpc_shutdown();
+}
+
+- (void)testSucceeds {
+  grpc_resolved_address resolved_addr;
+  struct sockaddr_in* addr = reinterpret_cast<struct sockaddr_in*>(resolved_addr.addr);
+  int svr_fd;
+  int r;
+  int connections_complete_before;
+  grpc_closure done;
+  grpc_core::ExecCtx exec_ctx;
+
+  gpr_log(GPR_DEBUG, "test_succeeds");
+
+  memset(&resolved_addr, 0, sizeof(resolved_addr));
+  resolved_addr.len = sizeof(struct sockaddr_in);
+  addr->sin_family = AF_INET;
+
+  /* create a dummy server */
+  svr_fd = socket(AF_INET, SOCK_STREAM, 0);
+  GPR_ASSERT(svr_fd >= 0);
+  GPR_ASSERT(0 == bind(svr_fd, (struct sockaddr*)addr, (socklen_t)resolved_addr.len));
+  GPR_ASSERT(0 == listen(svr_fd, 1));
+
+  gpr_mu_lock(&g_mu);
+  connections_complete_before = g_connections_complete;
+  gpr_mu_unlock(&g_mu);
+
+  /* connect to it */
+  GPR_ASSERT(getsockname(svr_fd, (struct sockaddr*)addr, (socklen_t*)&resolved_addr.len) == 0);
+  GRPC_CLOSURE_INIT(&done, must_succeed, nullptr, grpc_schedule_on_exec_ctx);
+  grpc_tcp_client_connect(&done, &g_connecting, nullptr, nullptr, &resolved_addr,
+                          GRPC_MILLIS_INF_FUTURE);
+
+  /* await the connection */
+  do {
+    resolved_addr.len = sizeof(addr);
+    r = accept(svr_fd, reinterpret_cast<struct sockaddr*>(addr),
+               reinterpret_cast<socklen_t*>(&resolved_addr.len));
+  } while (r == -1 && errno == EINTR);
+  GPR_ASSERT(r >= 0);
+  close(r);
+
+  grpc_core::ExecCtx::Get()->Flush();
+
+  /* wait for the connection callback to finish */
+  gpr_mu_lock(&g_mu);
+  NSDate* deadline = [NSDate dateWithTimeIntervalSinceNow:5];
+  while (connections_complete_before == g_connections_complete) {
+    gpr_mu_unlock(&g_mu);
+    [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:deadline];
+    gpr_mu_lock(&g_mu);
+  }
+  XCTAssertGreaterThan(g_connections_complete, connections_complete_before);
+
+  gpr_mu_unlock(&g_mu);
+}
+
+- (void)testFails {
+  grpc_core::ExecCtx exec_ctx;
+
+  grpc_resolved_address resolved_addr;
+  struct sockaddr_in* addr = reinterpret_cast<struct sockaddr_in*>(resolved_addr.addr);
+  int connections_complete_before;
+  grpc_closure done;
+  int svr_fd;
+
+  gpr_log(GPR_DEBUG, "test_fails");
+
+  memset(&resolved_addr, 0, sizeof(resolved_addr));
+  resolved_addr.len = static_cast<socklen_t>(sizeof(struct sockaddr_in));
+  addr->sin_family = AF_INET;
+
+  svr_fd = socket(AF_INET, SOCK_STREAM, 0);
+  GPR_ASSERT(svr_fd >= 0);
+  GPR_ASSERT(0 == bind(svr_fd, (struct sockaddr*)addr, (socklen_t)resolved_addr.len));
+  GPR_ASSERT(0 == listen(svr_fd, 1));
+  GPR_ASSERT(getsockname(svr_fd, (struct sockaddr*)addr, (socklen_t*)&resolved_addr.len) == 0);
+  close(svr_fd);
+
+  gpr_mu_lock(&g_mu);
+  connections_complete_before = g_connections_complete;
+  gpr_mu_unlock(&g_mu);
+
+  /* connect to a broken address */
+  GRPC_CLOSURE_INIT(&done, must_fail, nullptr, grpc_schedule_on_exec_ctx);
+  grpc_tcp_client_connect(&done, &g_connecting, nullptr, nullptr, &resolved_addr,
+                          GRPC_MILLIS_INF_FUTURE);
+
+  grpc_core::ExecCtx::Get()->Flush();
+
+  /* wait for the connection callback to finish */
+  gpr_mu_lock(&g_mu);
+  NSDate* deadline = [NSDate dateWithTimeIntervalSinceNow:5];
+  while (g_connections_complete == connections_complete_before) {
+    gpr_mu_unlock(&g_mu);
+    [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:deadline];
+    gpr_mu_lock(&g_mu);
+  }
+
+  XCTAssertGreaterThan(g_connections_complete, connections_complete_before);
+
+  gpr_mu_unlock(&g_mu);
+}
+
+@end
+
+#else  // GRPC_CFSTREAM
+
+// Dummy test suite
+@interface CFStreamClientTests : XCTestCase
+
+@end
+
+@implementation CFStreamClientTests
+
+- (void)setUp {
+  [super setUp];
+}
+
+- (void)tearDown {
+  [super tearDown];
+}
+
+@end
+
+#endif  // GRPC_CFSTREAM
diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamEndpointTests.mm b/test/core/iomgr/ios/CFStreamTests/CFStreamEndpointTests.mm
new file mode 100644
index 0000000..012804d
--- /dev/null
+++ b/test/core/iomgr/ios/CFStreamTests/CFStreamEndpointTests.mm
@@ -0,0 +1,344 @@
+/*
+ *
+ * Copyright 2018 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 <XCTest/XCTest.h>
+
+#include "src/core/lib/iomgr/port.h"
+
+#ifdef GRPC_CFSTREAM
+
+#include <netinet/in.h>
+
+#include <grpc/impl/codegen/sync.h>
+#include <grpc/support/sync.h>
+
+#include "src/core/lib/iomgr/endpoint.h"
+#include "src/core/lib/iomgr/resolve_address.h"
+#include "src/core/lib/iomgr/tcp_client.h"
+#include "test/core/util/test_config.h"
+
+static const int kConnectTimeout = 5;
+static const int kWriteTimeout = 5;
+static const int kReadTimeout = 5;
+
+static const int kBufferSize = 10000;
+
+static const int kRunLoopTimeout = 1;
+
+static void set_atm(void *arg, grpc_error *error) {
+  gpr_atm *p = static_cast<gpr_atm *>(arg);
+  gpr_atm_full_cas(p, -1, reinterpret_cast<gpr_atm>(error));
+}
+
+static void init_event_closure(grpc_closure *closure, gpr_atm *atm) {
+  *atm = -1;
+  GRPC_CLOSURE_INIT(closure, set_atm, static_cast<void *>(atm), grpc_schedule_on_exec_ctx);
+}
+
+static bool compare_slice_buffer_with_buffer(grpc_slice_buffer *slices, const char *buffer,
+                                             size_t buffer_len) {
+  if (slices->length != buffer_len) {
+    return false;
+  }
+
+  for (int i = 0; i < slices->count; i++) {
+    grpc_slice slice = slices->slices[i];
+    if (0 != memcmp(buffer, GRPC_SLICE_START_PTR(slice), GRPC_SLICE_LENGTH(slice))) {
+      return false;
+    }
+    buffer += GRPC_SLICE_LENGTH(slice);
+  }
+
+  return true;
+}
+
+@interface CFStreamEndpointTests : XCTestCase
+
+@end
+
+@implementation CFStreamEndpointTests {
+  grpc_endpoint *ep_;
+  int svr_fd_;
+}
+
+- (BOOL)waitForEvent:(gpr_atm *)event timeout:(int)timeout {
+  grpc_core::ExecCtx::Get()->Flush();
+
+  NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:kConnectTimeout];
+  while (gpr_atm_acq_load(event) == -1 && [deadline timeIntervalSinceNow] > 0) {
+    NSDate *deadline = [NSDate dateWithTimeIntervalSinceNow:kRunLoopTimeout];
+    [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:deadline];
+  }
+
+  return (gpr_atm_acq_load(event) != -1);
+}
+
++ (void)setUp {
+  grpc_init();
+}
+
++ (void)tearDown {
+  grpc_shutdown();
+}
+
+- (void)setUp {
+  self.continueAfterFailure = NO;
+
+  // Set up CFStream connection before testing the endpoint
+
+  grpc_core::ExecCtx exec_ctx;
+
+  grpc_resolved_address resolved_addr;
+  struct sockaddr_in *addr = reinterpret_cast<struct sockaddr_in *>(resolved_addr.addr);
+  int svr_fd;
+  int r;
+  gpr_atm connected = -1;
+  grpc_closure done;
+
+  gpr_log(GPR_DEBUG, "test_succeeds");
+
+  memset(&resolved_addr, 0, sizeof(resolved_addr));
+  resolved_addr.len = sizeof(struct sockaddr_in);
+  addr->sin_family = AF_INET;
+
+  /* create a dummy server */
+  svr_fd = socket(AF_INET, SOCK_STREAM, 0);
+  XCTAssertGreaterThanOrEqual(svr_fd, 0);
+  XCTAssertEqual(bind(svr_fd, (struct sockaddr *)addr, (socklen_t)resolved_addr.len), 0);
+  XCTAssertEqual(listen(svr_fd, 1), 0);
+
+  /* connect to it */
+  XCTAssertEqual(getsockname(svr_fd, (struct sockaddr *)addr, (socklen_t *)&resolved_addr.len), 0);
+  init_event_closure(&done, &connected);
+  grpc_tcp_client_connect(&done, &ep_, nullptr, nullptr, &resolved_addr, GRPC_MILLIS_INF_FUTURE);
+
+  /* await the connection */
+  do {
+    resolved_addr.len = sizeof(addr);
+    r = accept(svr_fd, reinterpret_cast<struct sockaddr *>(addr),
+               reinterpret_cast<socklen_t *>(&resolved_addr.len));
+  } while (r == -1 && errno == EINTR);
+  XCTAssertGreaterThanOrEqual(r, 0);
+  svr_fd_ = r;
+
+  /* wait for the connection callback to finish */
+  XCTAssertEqual([self waitForEvent:&connected timeout:kConnectTimeout], YES);
+  XCTAssertEqual(reinterpret_cast<grpc_error *>(connected), GRPC_ERROR_NONE);
+}
+
+- (void)tearDown {
+  grpc_core::ExecCtx exec_ctx;
+  close(svr_fd_);
+  grpc_endpoint_destroy(ep_);
+}
+
+- (void)testReadWrite {
+  grpc_core::ExecCtx exec_ctx;
+
+  gpr_atm read;
+  grpc_closure read_done;
+  grpc_slice_buffer read_slices;
+  grpc_slice_buffer read_one_slice;
+  gpr_atm write;
+  grpc_closure write_done;
+  grpc_slice_buffer write_slices;
+
+  grpc_slice slice;
+  char write_buffer[kBufferSize];
+  char read_buffer[kBufferSize];
+  size_t recv_size = 0;
+
+  grpc_slice_buffer_init(&write_slices);
+  slice = grpc_slice_from_static_buffer(write_buffer, kBufferSize);
+  grpc_slice_buffer_add(&write_slices, slice);
+  init_event_closure(&write_done, &write);
+  grpc_endpoint_write(ep_, &write_slices, &write_done);
+
+  XCTAssertEqual([self waitForEvent:&write timeout:kWriteTimeout], YES);
+  XCTAssertEqual(reinterpret_cast<grpc_error *>(write), GRPC_ERROR_NONE);
+
+  while (recv_size < kBufferSize) {
+    ssize_t size = recv(svr_fd_, read_buffer, kBufferSize, 0);
+    XCTAssertGreaterThanOrEqual(size, 0);
+    recv_size += size;
+  }
+
+  XCTAssertEqual(recv_size, kBufferSize);
+  XCTAssertEqual(memcmp(read_buffer, write_buffer, kBufferSize), 0);
+  ssize_t send_size = send(svr_fd_, read_buffer, kBufferSize, 0);
+  XCTAssertGreaterThanOrEqual(send_size, 0);
+
+  grpc_slice_buffer_init(&read_slices);
+  grpc_slice_buffer_init(&read_one_slice);
+  while (read_slices.length < kBufferSize) {
+    init_event_closure(&read_done, &read);
+    grpc_endpoint_read(ep_, &read_one_slice, &read_done);
+    XCTAssertEqual([self waitForEvent:&read timeout:kReadTimeout], YES);
+    XCTAssertEqual(reinterpret_cast<grpc_error *>(read), GRPC_ERROR_NONE);
+    grpc_slice_buffer_move_into(&read_one_slice, &read_slices);
+    XCTAssertLessThanOrEqual(read_slices.length, kBufferSize);
+  }
+  XCTAssertTrue(compare_slice_buffer_with_buffer(&read_slices, read_buffer, kBufferSize));
+
+  grpc_endpoint_shutdown(ep_, GRPC_ERROR_NONE);
+  grpc_slice_buffer_reset_and_unref(&read_slices);
+  grpc_slice_buffer_reset_and_unref(&write_slices);
+  grpc_slice_buffer_reset_and_unref(&read_one_slice);
+}
+
+- (void)testShutdownBeforeRead {
+  grpc_core::ExecCtx exec_ctx;
+
+  gpr_atm read;
+  grpc_closure read_done;
+  grpc_slice_buffer read_slices;
+  gpr_atm write;
+  grpc_closure write_done;
+  grpc_slice_buffer write_slices;
+
+  grpc_slice slice;
+  char write_buffer[kBufferSize];
+  char read_buffer[kBufferSize];
+  size_t recv_size = 0;
+
+  grpc_slice_buffer_init(&read_slices);
+  init_event_closure(&read_done, &read);
+  grpc_endpoint_read(ep_, &read_slices, &read_done);
+
+  grpc_slice_buffer_init(&write_slices);
+  slice = grpc_slice_from_static_buffer(write_buffer, kBufferSize);
+  grpc_slice_buffer_add(&write_slices, slice);
+  init_event_closure(&write_done, &write);
+  grpc_endpoint_write(ep_, &write_slices, &write_done);
+
+  XCTAssertEqual([self waitForEvent:&write timeout:kWriteTimeout], YES);
+  XCTAssertEqual(reinterpret_cast<grpc_error *>(write), GRPC_ERROR_NONE);
+
+  while (recv_size < kBufferSize) {
+    ssize_t size = recv(svr_fd_, read_buffer, kBufferSize, 0);
+    XCTAssertGreaterThanOrEqual(size, 0);
+    recv_size += size;
+  }
+
+  XCTAssertEqual(recv_size, kBufferSize);
+  XCTAssertEqual(memcmp(read_buffer, write_buffer, kBufferSize), 0);
+
+  XCTAssertEqual([self waitForEvent:&read timeout:kReadTimeout], NO);
+
+  grpc_endpoint_shutdown(ep_, GRPC_ERROR_NONE);
+
+  grpc_core::ExecCtx::Get()->Flush();
+  XCTAssertEqual([self waitForEvent:&read timeout:kReadTimeout], YES);
+  XCTAssertNotEqual(reinterpret_cast<grpc_error *>(read), GRPC_ERROR_NONE);
+
+  grpc_slice_buffer_reset_and_unref(&read_slices);
+  grpc_slice_buffer_reset_and_unref(&write_slices);
+}
+
+- (void)testRemoteClosed {
+  grpc_core::ExecCtx exec_ctx;
+
+  gpr_atm read;
+  grpc_closure read_done;
+  grpc_slice_buffer read_slices;
+  gpr_atm write;
+  grpc_closure write_done;
+  grpc_slice_buffer write_slices;
+
+  grpc_slice slice;
+  char write_buffer[kBufferSize];
+  char read_buffer[kBufferSize];
+  size_t recv_size = 0;
+
+  init_event_closure(&read_done, &read);
+  grpc_slice_buffer_init(&read_slices);
+  grpc_endpoint_read(ep_, &read_slices, &read_done);
+
+  grpc_slice_buffer_init(&write_slices);
+  slice = grpc_slice_from_static_buffer(write_buffer, kBufferSize);
+  grpc_slice_buffer_add(&write_slices, slice);
+  init_event_closure(&write_done, &write);
+  grpc_endpoint_write(ep_, &write_slices, &write_done);
+
+  XCTAssertEqual([self waitForEvent:&write timeout:kWriteTimeout], YES);
+  XCTAssertEqual(reinterpret_cast<grpc_error *>(write), GRPC_ERROR_NONE);
+
+  while (recv_size < kBufferSize) {
+    ssize_t size = recv(svr_fd_, read_buffer, kBufferSize, 0);
+    XCTAssertGreaterThanOrEqual(size, 0);
+    recv_size += size;
+  }
+
+  XCTAssertEqual(recv_size, kBufferSize);
+  XCTAssertEqual(memcmp(read_buffer, write_buffer, kBufferSize), 0);
+
+  close(svr_fd_);
+
+  XCTAssertEqual([self waitForEvent:&read timeout:kReadTimeout], YES);
+  XCTAssertNotEqual(reinterpret_cast<grpc_error *>(read), GRPC_ERROR_NONE);
+
+  grpc_endpoint_shutdown(ep_, GRPC_ERROR_NONE);
+  grpc_slice_buffer_reset_and_unref(&read_slices);
+  grpc_slice_buffer_reset_and_unref(&write_slices);
+}
+
+- (void)testRemoteReset {
+  grpc_core::ExecCtx exec_ctx;
+
+  gpr_atm read;
+  grpc_closure read_done;
+  grpc_slice_buffer read_slices;
+
+  init_event_closure(&read_done, &read);
+  grpc_slice_buffer_init(&read_slices);
+  grpc_endpoint_read(ep_, &read_slices, &read_done);
+
+  struct linger so_linger;
+  so_linger.l_onoff = 1;
+  so_linger.l_linger = 0;
+  setsockopt(svr_fd_, SOL_SOCKET, SO_LINGER, &so_linger, sizeof(so_linger));
+
+  close(svr_fd_);
+
+  XCTAssertEqual([self waitForEvent:&read timeout:kReadTimeout], YES);
+  XCTAssertNotEqual(reinterpret_cast<grpc_error *>(read), GRPC_ERROR_NONE);
+
+  grpc_endpoint_shutdown(ep_, GRPC_ERROR_NONE);
+  grpc_slice_buffer_reset_and_unref(&read_slices);
+}
+
+@end
+
+#else  // GRPC_CFSTREAM
+
+// Dummy test suite
+@interface CFStreamEndpointTests : XCTestCase
+@end
+
+@implementation CFStreamEndpointTests
+-(void)setUp {
+  [super setUp];
+}
+
+- (void)tearDown {
+  [super tearDown];
+}
+
+@end
+
+#endif  // GRPC_CFSTREAM
diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/project.pbxproj b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..2218f12
--- /dev/null
+++ b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/project.pbxproj
@@ -0,0 +1,338 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 48;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		5E143B892069D72200715A6E /* CFStreamClientTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5E143B882069D72200715A6E /* CFStreamClientTests.mm */; };
+		5E143B8C206B5F9F00715A6E /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 5E143B8A2069D72700715A6E /* Info.plist */; };
+		5E143B8E206C5B9A00715A6E /* CFStreamEndpointTests.mm in Sources */ = {isa = PBXBuildFile; fileRef = 5E143B8D206C5B9A00715A6E /* CFStreamEndpointTests.mm */; };
+		604EA96D9CD477A8EA411BDF /* libPods-CFStreamTests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AFFA154D492751CEAC05D591 /* libPods-CFStreamTests.a */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXFileReference section */
+		5E143B792069D67300715A6E /* CFStreamTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CFStreamTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
+		5E143B882069D72200715A6E /* CFStreamClientTests.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = CFStreamClientTests.mm; sourceTree = "<group>"; };
+		5E143B8A2069D72700715A6E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
+		5E143B8D206C5B9A00715A6E /* CFStreamEndpointTests.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = CFStreamEndpointTests.mm; sourceTree = "<group>"; };
+		8CB4409E07E180CCA59987DF /* Pods-CFStreamTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CFStreamTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-CFStreamTests/Pods-CFStreamTests.release.xcconfig"; sourceTree = "<group>"; };
+		9E3FAF9DA6B98ED4FE6D6848 /* Pods-CFStreamTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-CFStreamTests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-CFStreamTests/Pods-CFStreamTests.debug.xcconfig"; sourceTree = "<group>"; };
+		AFFA154D492751CEAC05D591 /* libPods-CFStreamTests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-CFStreamTests.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		5E143B762069D67300715A6E /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				604EA96D9CD477A8EA411BDF /* libPods-CFStreamTests.a in Frameworks */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		5E143B582069D67300715A6E = {
+			isa = PBXGroup;
+			children = (
+				5E143B8D206C5B9A00715A6E /* CFStreamEndpointTests.mm */,
+				5E143B8A2069D72700715A6E /* Info.plist */,
+				5E143B882069D72200715A6E /* CFStreamClientTests.mm */,
+				5E143B622069D67300715A6E /* Products */,
+				A331D95F7F230B507FBF6D22 /* Pods */,
+				6AC36F6C5DB5CA8F717D1B67 /* Frameworks */,
+			);
+			sourceTree = "<group>";
+		};
+		5E143B622069D67300715A6E /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				5E143B792069D67300715A6E /* CFStreamTests.xctest */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		6AC36F6C5DB5CA8F717D1B67 /* Frameworks */ = {
+			isa = PBXGroup;
+			children = (
+				AFFA154D492751CEAC05D591 /* libPods-CFStreamTests.a */,
+			);
+			name = Frameworks;
+			sourceTree = "<group>";
+		};
+		A331D95F7F230B507FBF6D22 /* Pods */ = {
+			isa = PBXGroup;
+			children = (
+				9E3FAF9DA6B98ED4FE6D6848 /* Pods-CFStreamTests.debug.xcconfig */,
+				8CB4409E07E180CCA59987DF /* Pods-CFStreamTests.release.xcconfig */,
+			);
+			name = Pods;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		5E143B782069D67300715A6E /* CFStreamTests */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = 5E143B852069D67300715A6E /* Build configuration list for PBXNativeTarget "CFStreamTests" */;
+			buildPhases = (
+				4EBA55D3E23FC6C84596E3D5 /* [CP] Check Pods Manifest.lock */,
+				5E143B752069D67300715A6E /* Sources */,
+				5E143B762069D67300715A6E /* Frameworks */,
+				5E143B772069D67300715A6E /* Resources */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = CFStreamTests;
+			productName = CFStreamTestsTests;
+			productReference = 5E143B792069D67300715A6E /* CFStreamTests.xctest */;
+			productType = "com.apple.product-type.bundle.unit-test";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		5E143B592069D67300715A6E /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0920;
+				ORGANIZATIONNAME = gRPC;
+				TargetAttributes = {
+					5E143B782069D67300715A6E = {
+						CreatedOnToolsVersion = 9.2;
+						ProvisioningStyle = Automatic;
+					};
+				};
+			};
+			buildConfigurationList = 5E143B5C2069D67300715A6E /* Build configuration list for PBXProject "CFStreamTests" */;
+			compatibilityVersion = "Xcode 8.0";
+			developmentRegion = en;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+				Base,
+			);
+			mainGroup = 5E143B582069D67300715A6E;
+			productRefGroup = 5E143B622069D67300715A6E /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				5E143B782069D67300715A6E /* CFStreamTests */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXResourcesBuildPhase section */
+		5E143B772069D67300715A6E /* Resources */ = {
+			isa = PBXResourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5E143B8C206B5F9F00715A6E /* Info.plist in Resources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXResourcesBuildPhase section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		4EBA55D3E23FC6C84596E3D5 /* [CP] Check Pods Manifest.lock */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+				"${PODS_ROOT}/Manifest.lock",
+			);
+			name = "[CP] Check Pods Manifest.lock";
+			outputPaths = (
+				"$(DERIVED_FILE_DIR)/Pods-CFStreamTests-checkManifestLockResult.txt",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n    # print error to STDERR\n    echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n    exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+			showEnvVarsInLog = 0;
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		5E143B752069D67300715A6E /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				5E143B892069D72200715A6E /* CFStreamClientTests.mm in Sources */,
+				5E143B8E206C5B9A00715A6E /* CFStreamEndpointTests.mm in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		5E143B802069D67300715A6E /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.2;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = iphoneos;
+			};
+			name = Debug;
+		};
+		5E143B812069D67300715A6E /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_ANALYZER_NONNULL = YES;
+				CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_COMMA = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INFINITE_RECURSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+				CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+				CLANG_WARN_STRICT_PROTOTYPES = YES;
+				CLANG_WARN_SUSPICIOUS_MOVE = YES;
+				CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "iPhone Developer";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu11;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				IPHONEOS_DEPLOYMENT_TARGET = 11.2;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = iphoneos;
+				VALIDATE_PRODUCT = YES;
+			};
+			name = Release;
+		};
+		5E143B862069D67300715A6E /* Debug */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 9E3FAF9DA6B98ED4FE6D6848 /* Pods-CFStreamTests.debug.xcconfig */;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"$(inherited)",
+					"COCOAPODS=1",
+					"$(inherited)",
+					"PB_FIELD_32BIT=1",
+					"PB_NO_PACKED_STRUCTS=1",
+					"GRPC_CFSTREAM=1",
+				);
+				INFOPLIST_FILE = Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.CFStreamTestsTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				USER_HEADER_SEARCH_PATHS = ../../../../..;
+			};
+			name = Debug;
+		};
+		5E143B872069D67300715A6E /* Release */ = {
+			isa = XCBuildConfiguration;
+			baseConfigurationReference = 8CB4409E07E180CCA59987DF /* Pods-CFStreamTests.release.xcconfig */;
+			buildSettings = {
+				CODE_SIGN_STYLE = Automatic;
+				INFOPLIST_FILE = Info.plist;
+				LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
+				PRODUCT_BUNDLE_IDENTIFIER = io.grpc.CFStreamTestsTests;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				TARGETED_DEVICE_FAMILY = "1,2";
+				USER_HEADER_SEARCH_PATHS = ../../../../..;
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		5E143B5C2069D67300715A6E /* Build configuration list for PBXProject "CFStreamTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5E143B802069D67300715A6E /* Debug */,
+				5E143B812069D67300715A6E /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		5E143B852069D67300715A6E /* Build configuration list for PBXNativeTarget "CFStreamTests" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				5E143B862069D67300715A6E /* Debug */,
+				5E143B872069D67300715A6E /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = 5E143B592069D67300715A6E /* Project object */;
+}
diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests.xcscheme b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests.xcscheme
new file mode 100644
index 0000000..25d6f78
--- /dev/null
+++ b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests.xcscheme
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0920"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "5E143B782069D67300715A6E"
+               BuildableName = "CFStreamTests.xctest"
+               BlueprintName = "CFStreamTests"
+               ReferencedContainer = "container:CFStreamTests.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Asan.xcscheme b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Asan.xcscheme
new file mode 100644
index 0000000..6c5c43a
--- /dev/null
+++ b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Asan.xcscheme
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0920"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      enableAddressSanitizer = "YES"
+      enableASanStackUseAfterReturn = "YES"
+      language = ""
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "5E143B782069D67300715A6E"
+               BuildableName = "CFStreamTests.xctest"
+               BlueprintName = "CFStreamTests"
+               ReferencedContainer = "container:CFStreamTests.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      enableASanStackUseAfterReturn = "YES"
+      language = ""
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Msan.xcscheme b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Msan.xcscheme
new file mode 100644
index 0000000..3e39ff8
--- /dev/null
+++ b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Msan.xcscheme
@@ -0,0 +1,78 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0920"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "5E143B782069D67300715A6E"
+               BuildableName = "CFStreamTests.xctest"
+               BlueprintName = "CFStreamTests"
+               ReferencedContainer = "container:CFStreamTests.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <AdditionalOptions>
+         <AdditionalOption
+            key = "DYLD_INSERT_LIBRARIES"
+            value = "/usr/lib/libgmalloc.dylib"
+            isEnabled = "YES">
+         </AdditionalOption>
+         <AdditionalOption
+            key = "NSZombieEnabled"
+            value = "YES"
+            isEnabled = "YES">
+         </AdditionalOption>
+         <AdditionalOption
+            key = "MallocGuardEdges"
+            value = ""
+            isEnabled = "YES">
+         </AdditionalOption>
+         <AdditionalOption
+            key = "MallocScribble"
+            value = ""
+            isEnabled = "YES">
+         </AdditionalOption>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Tsan.xcscheme b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Tsan.xcscheme
new file mode 100644
index 0000000..f0bde83
--- /dev/null
+++ b/test/core/iomgr/ios/CFStreamTests/CFStreamTests.xcodeproj/xcshareddata/xcschemes/CFStreamTests_Tsan.xcscheme
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<Scheme
+   LastUpgradeVersion = "0920"
+   version = "1.3">
+   <BuildAction
+      parallelizeBuildables = "YES"
+      buildImplicitDependencies = "YES">
+   </BuildAction>
+   <TestAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      enableThreadSanitizer = "YES"
+      language = ""
+      shouldUseLaunchSchemeArgsEnv = "YES">
+      <Testables>
+         <TestableReference
+            skipped = "NO">
+            <BuildableReference
+               BuildableIdentifier = "primary"
+               BlueprintIdentifier = "5E143B782069D67300715A6E"
+               BuildableName = "CFStreamTests.xctest"
+               BlueprintName = "CFStreamTests"
+               ReferencedContainer = "container:CFStreamTests.xcodeproj">
+            </BuildableReference>
+         </TestableReference>
+      </Testables>
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </TestAction>
+   <LaunchAction
+      buildConfiguration = "Debug"
+      selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
+      selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
+      language = ""
+      launchStyle = "0"
+      useCustomWorkingDirectory = "NO"
+      ignoresPersistentStateOnLaunch = "NO"
+      debugDocumentVersioning = "YES"
+      stopOnEveryThreadSanitizerIssue = "YES"
+      debugServiceExtension = "internal"
+      allowLocationSimulation = "YES">
+      <AdditionalOptions>
+      </AdditionalOptions>
+   </LaunchAction>
+   <ProfileAction
+      buildConfiguration = "Release"
+      shouldUseLaunchSchemeArgsEnv = "YES"
+      savedToolIdentifier = ""
+      useCustomWorkingDirectory = "NO"
+      debugDocumentVersioning = "YES">
+   </ProfileAction>
+   <AnalyzeAction
+      buildConfiguration = "Debug">
+   </AnalyzeAction>
+   <ArchiveAction
+      buildConfiguration = "Release"
+      revealArchiveInOrganizer = "YES">
+   </ArchiveAction>
+</Scheme>
diff --git a/test/core/iomgr/ios/CFStreamTests/Info.plist b/test/core/iomgr/ios/CFStreamTests/Info.plist
new file mode 100644
index 0000000..6c40a6c
--- /dev/null
+++ b/test/core/iomgr/ios/CFStreamTests/Info.plist
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
+<plist version="1.0">
+<dict>
+	<key>CFBundleDevelopmentRegion</key>
+	<string>$(DEVELOPMENT_LANGUAGE)</string>
+	<key>CFBundleExecutable</key>
+	<string>$(EXECUTABLE_NAME)</string>
+	<key>CFBundleIdentifier</key>
+	<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
+	<key>CFBundleInfoDictionaryVersion</key>
+	<string>6.0</string>
+	<key>CFBundleName</key>
+	<string>$(PRODUCT_NAME)</string>
+	<key>CFBundlePackageType</key>
+	<string>BNDL</string>
+	<key>CFBundleShortVersionString</key>
+	<string>1.0</string>
+	<key>CFBundleVersion</key>
+	<string>1</string>
+</dict>
+</plist>
diff --git a/test/core/iomgr/ios/CFStreamTests/Podfile b/test/core/iomgr/ios/CFStreamTests/Podfile
new file mode 100644
index 0000000..630168a
--- /dev/null
+++ b/test/core/iomgr/ios/CFStreamTests/Podfile
@@ -0,0 +1,50 @@
+source 'https://github.com/CocoaPods/Specs.git'
+platform :ios, '8.0'
+
+install! 'cocoapods', :deterministic_uuids => false
+
+# Location of gRPC's repo root relative to this file.
+GRPC_LOCAL_SRC = '../../../../..'
+
+# Install the dependencies in the main target plus all test targets.
+target 'CFStreamTests' do
+  pod 'gRPC-Core/CFStream-Implementation', :path => GRPC_LOCAL_SRC
+end
+
+pre_install do |installer|
+  # This is the gRPC-Core podspec object, as initialized by its podspec file.
+  grpc_core_spec = installer.pod_targets.find{|t| t.name == 'gRPC-Core'}.root_spec
+
+  # Copied from gRPC-Core.podspec, except for the adjusted src_root:
+  src_root = "$(PODS_ROOT)/../#{GRPC_LOCAL_SRC}"
+  grpc_core_spec.pod_target_xcconfig = {
+    'GRPC_SRC_ROOT' => src_root,
+    'HEADER_SEARCH_PATHS' => '"$(inherited)" "$(GRPC_SRC_ROOT)/include"',
+    'USER_HEADER_SEARCH_PATHS' => '"$(GRPC_SRC_ROOT)"',
+    # If we don't set these two settings, `include/grpc/support/time.h` and
+    # `src/core/lib/gpr/string.h` shadow the system `<time.h>` and `<string.h>`, breaking the
+    # build.
+    'USE_HEADERMAP' => 'NO',
+    'ALWAYS_SEARCH_USER_PATHS' => 'NO',
+  }
+end
+
+post_install do |installer|
+  installer.pods_project.targets.each do |target|
+    target.build_configurations.each do |config|
+      config.build_settings['GCC_TREAT_WARNINGS_AS_ERRORS'] = 'YES'
+    end
+
+    # CocoaPods creates duplicated library targets of gRPC-Core when the test targets include
+    # non-default subspecs of gRPC-Core. All of these library targets start with prefix 'gRPC-Core'
+    # and require the same error suppresion.
+    if target.name.start_with?('gRPC-Core')
+      target.build_configurations.each do |config|
+        # TODO(zyc): Remove this setting after the issue is resolved
+        # GPR_UNREACHABLE_CODE causes "Control may reach end of non-void
+        # function" warning
+        config.build_settings['GCC_WARN_ABOUT_RETURN_TYPE'] = 'NO'
+      end
+    end
+  end
+end
diff --git a/test/core/iomgr/ios/CFStreamTests/build_tests.sh b/test/core/iomgr/ios/CFStreamTests/build_tests.sh
new file mode 100755
index 0000000..d23f26f
--- /dev/null
+++ b/test/core/iomgr/ios/CFStreamTests/build_tests.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+# Copyright 2018 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.
+
+# Don't run this script standalone. Instead, run from the repository root:
+# ./tools/run_tests/run_tests.py -l objc
+
+set -e
+
+# CocoaPods requires the terminal to be using UTF-8 encoding.
+export LANG=en_US.UTF-8
+
+cd "$(dirname "$0")"
+
+hash pod 2>/dev/null || { echo >&2 "Cocoapods needs to be installed."; exit 1; }
+hash xcodebuild 2>/dev/null || {
+    echo >&2 "XCode command-line tools need to be installed."
+    exit 1
+}
+
+# clean the directory
+rm -rf Pods
+rm -rf CFStreamTests.xcworkspace
+rm -f Podfile.lock
+
+echo "TIME:  $(date)"
+pod install
+
diff --git a/test/core/iomgr/ios/CFStreamTests/run_tests.sh b/test/core/iomgr/ios/CFStreamTests/run_tests.sh
new file mode 100755
index 0000000..1045ec1
--- /dev/null
+++ b/test/core/iomgr/ios/CFStreamTests/run_tests.sh
@@ -0,0 +1,67 @@
+#!/bin/bash
+# Copyright 2018 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.
+
+# Don't run this script standalone. Instead, run from the repository root:
+# ./tools/run_tests/run_tests.py -l objc
+
+set -ev
+
+cd "$(dirname "$0")"
+
+echo "TIME:  $(date)"
+
+XCODEBUILD_FILTER='(^CompileC |^Ld |^ *[^ ]*clang |^ *cd |^ *export |^Libtool |^ *[^ ]*libtool |^CpHeader |^ *builtin-copy )'
+
+xcodebuild \
+    -workspace CFStreamTests.xcworkspace \
+    -scheme CFStreamTests \
+    -destination name="iPhone 8" \
+    test \
+    | egrep -v "$XCODEBUILD_FILTER" \
+    | egrep -v '^$' \
+    | egrep -v "(GPBDictionary|GPBArray)" -
+
+echo "TIME:  $(date)"
+
+xcodebuild \
+    -workspace CFStreamTests.xcworkspace \
+    -scheme CFStreamTests_Asan \
+    -destination name="iPhone 8" \
+    test \
+    | egrep -v "$XCODEBUILD_FILTER" \
+    | egrep -v '^$' \
+    | egrep -v "(GPBDictionary|GPBArray)" -
+
+echo "TIME:  $(date)"
+
+xcodebuild \
+    -workspace CFStreamTests.xcworkspace \
+    -scheme CFStreamTests_Tsan \
+    -destination name="iPhone 8" \
+    test \
+    | egrep -v "$XCODEBUILD_FILTER" \
+    | egrep -v '^$' \
+    | egrep -v "(GPBDictionary|GPBArray)" -
+
+echo "TIME:  $(date)"
+
+xcodebuild \
+    -workspace CFStreamTests.xcworkspace \
+    -scheme CFStreamTests_Msan \
+    -destination name="iPhone 8" \
+    test \
+    | egrep -v "$XCODEBUILD_FILTER" \
+    | egrep -v '^$' \
+    | egrep -v "(GPBDictionary|GPBArray)" -
diff --git a/tools/run_tests/run_tests.py b/tools/run_tests/run_tests.py
index 6fb0f89..6c60ff8 100755
--- a/tools/run_tests/run_tests.py
+++ b/tools/run_tests/run_tests.py
@@ -1102,6 +1102,12 @@
                     'SCHEME': 'SwiftSample',
                     'EXAMPLE_PATH': 'src/objective-c/examples/SwiftSample'
                 }),
+            self.config.job_spec(
+                ['test/core/iomgr/ios/CFStreamTests/run_tests.sh'],
+                timeout_seconds=10 * 60,
+                shortname='cfstream-tests',
+                cpu_cost=1e6,
+                environ=_FORCE_ENVIRON_FOR_WRAPPERS),
         ]
 
     def pre_build_steps(self):
@@ -1114,7 +1120,10 @@
         return []
 
     def build_steps(self):
-        return [['src/objective-c/tests/build_tests.sh']]
+        return [
+            ['src/objective-c/tests/build_tests.sh'],
+            ['test/core/iomgr/ios/CFStreamTests/build_tests.sh'],
+        ]
 
     def post_tests_steps(self):
         return []