[ObjC] unit tests with bazel (#29799)

* unit tests with bazel

* passing via --test_env from bazel command line

* remove env from BUILD; fix sanity check in run_one_test_bazel.sh

* add port server
diff --git a/src/objective-c/tests/UnitTests/GRPCClientTests.m b/src/objective-c/tests/UnitTests/GRPCClientTests.m
index de687e4..8f09443 100644
--- a/src/objective-c/tests/UnitTests/GRPCClientTests.m
+++ b/src/objective-c/tests/UnitTests/GRPCClientTests.m
@@ -33,6 +33,7 @@
 
 #include <netinet/in.h>
 
+#import "../Common/TestUtils.h"
 #import "../version.h"
 
 #define TEST_TIMEOUT 16
@@ -41,7 +42,6 @@
 // in turn derived from environment variable of the same name.
 #define NSStringize_helper(x) #x
 #define NSStringize(x) @NSStringize_helper(x)
-static NSString *const kHostAddress = NSStringize(HOST_PORT_LOCAL);
 static NSString *const kPackage = @"grpc.testing";
 static NSString *const kService = @"TestService";
 
@@ -108,10 +108,10 @@
 
 - (void)setUp {
   // Add a custom user agent prefix and suffix that will be used in test
-  [GRPCCall setUserAgentPrefix:@"Foo" forHost:kHostAddress];
-  [GRPCCall setUserAgentSuffix:@"Suffix" forHost:kHostAddress];
+  [GRPCCall setUserAgentPrefix:@"Foo" forHost:GRPCGetLocalInteropTestServerAddressPlainText()];
+  [GRPCCall setUserAgentSuffix:@"Suffix" forHost:GRPCGetLocalInteropTestServerAddressPlainText()];
   // Register test server as non-SSL.
-  [GRPCCall useInsecureConnectionsForHost:kHostAddress];
+  [GRPCCall useInsecureConnectionsForHost:GRPCGetLocalInteropTestServerAddressPlainText()];
 
   // This method isn't implemented by the remote server.
   kInexistentMethod = [[GRPCProtoMethod alloc] initWithPackage:kPackage
@@ -131,7 +131,7 @@
 - (void)testConnectionToRemoteServer {
   __weak XCTestExpectation *expectation = [self expectationWithDescription:@"Server reachable."];
 
-  GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
+  GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
                                              path:kInexistentMethod.HTTPPath
                                    requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
 
@@ -155,7 +155,7 @@
       [self expectationWithDescription:@"Empty response received."];
   __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
 
-  GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
+  GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
                                              path:kEmptyCallMethod.HTTPPath
                                    requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
 
@@ -185,7 +185,7 @@
   request.fillOauthScope = YES;
   GRXWriter *requestsWriter = [GRXWriter writerWithValue:[request data]];
 
-  GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
+  GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
                                              path:kUnaryCallMethod.HTTPPath
                                    requestsWriter:requestsWriter];
 
@@ -215,7 +215,7 @@
   __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
   __weak XCTestExpectation *metadata = [self expectationWithDescription:@"Metadata changed."];
 
-  GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
+  GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
                                              path:kEmptyCallMethod.HTTPPath
                                    requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
 
@@ -249,7 +249,7 @@
       [self expectationWithDescription:@"Empty response received."];
   __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
 
-  GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
+  GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
                                              path:kEmptyCallMethod.HTTPPath
                                    requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
   // Setting this special key in the header will cause the interop server to echo back the
@@ -309,7 +309,7 @@
       [self expectationWithDescription:@"Empty response received."];
   __weak XCTestExpectation *completion = [self expectationWithDescription:@"Empty RPC completed."];
 
-  GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
+  GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
                                              path:kEmptyCallMethod.HTTPPath
                                    requestsWriter:[GRXWriter writerWithValue:[NSData data]]];
   // Setting this special key in the header will cause the interop server to echo back the
@@ -350,7 +350,7 @@
   GRXWriter *requestsWriter = [GRXWriter emptyWriter];
   [requestsWriter finishWithError:nil];
   @try {
-    (void)[[GRPCCall alloc] initWithHost:kHostAddress
+    (void)[[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
                                     path:kUnaryCallMethod.HTTPPath
                           requestsWriter:requestsWriter];
     XCTFail(@"Did not receive an exception when GRXWriter has incorrect state.");
@@ -373,7 +373,7 @@
 
   GRXWriter *requestsWriter1 = [GRXWriter writerWithValue:[request data]];
 
-  GRPCCall *call1 = [[GRPCCall alloc] initWithHost:kHostAddress
+  GRPCCall *call1 = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
                                               path:kUnaryCallMethod.HTTPPath
                                     requestsWriter:requestsWriter1];
 
@@ -401,7 +401,7 @@
 
   GRXWriter *requestsWriter2 = [GRXWriter writerWithValue:[request data]];
 
-  GRPCCall *call2 = [[GRPCCall alloc] initWithHost:kHostAddress
+  GRPCCall *call2 = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
                                               path:kUnaryCallMethod.HTTPPath
                                     requestsWriter:requestsWriter2];
 
@@ -427,7 +427,7 @@
   __weak XCTestExpectation *completion = [self expectationWithDescription:@"RPC completed."];
 
   GRXBufferedPipe *pipe = [GRXBufferedPipe pipe];
-  GRPCCall *call = [[GRPCCall alloc] initWithHost:kHostAddress
+  GRPCCall *call = [[GRPCCall alloc] initWithHost:GRPCGetLocalInteropTestServerAddressPlainText()
                                              path:kFullDuplexCallMethod.HTTPPath
                                    requestsWriter:pipe];
 
diff --git a/src/objective-c/tests/run_one_test_bazel.sh b/src/objective-c/tests/run_one_test_bazel.sh
index 09da7bf..2cbca24 100755
--- a/src/objective-c/tests/run_one_test_bazel.sh
+++ b/src/objective-c/tests/run_one_test_bazel.sh
@@ -27,12 +27,8 @@
 
 INTEROP=../../../bazel-out/darwin-fastbuild/bin/test/cpp/interop/interop_server
 
-[ -d Tests.xcworkspace ] || {
-    ./build_tests.sh
-}
-
 [ -f $INTEROP ] || {
-    BAZEL build //test/cpp/interop:interop_server
+    $BAZEL build //test/cpp/interop:interop_server
 }
 
 [ -z "$(ps aux |egrep 'port_server\.py.*-p\s32766')" ] && {
@@ -48,4 +44,7 @@
 
 trap 'kill -9 `jobs -p` ; echo "EXIT TIME:  $(date)"' EXIT
 
-../../../tools/bazel run $SCHEME
+time $BAZEL run \
+    --test_env HOST_PORT_LOCALSSL=localhost:$TLS_PORT \
+    --test_env HOST_PORT_LOCAL=localhost:$PLAIN_PORT \
+    $SCHEME
diff --git a/tools/internal_ci/macos/grpc_objc_bazel_test.sh b/tools/internal_ci/macos/grpc_objc_bazel_test.sh
index 2ed9c7c..9ecb023 100755
--- a/tools/internal_ci/macos/grpc_objc_bazel_test.sh
+++ b/tools/internal_ci/macos/grpc_objc_bazel_test.sh
@@ -69,11 +69,17 @@
   -- \
   //test/cpp/interop:interop_server
 
+# Start port server and allocate ports to run interop_server
+python3 tools/run_tests/start_port_server.py
+
+PLAIN_PORT=$(curl localhost:32766/get)
+TLS_PORT=$(curl localhost:32766/get)
+
 INTEROP_SERVER_BINARY=bazel-bin/test/cpp/interop/interop_server
 # run the interop server on the background. The port numbers must match TestConfigs in BUILD.
 # TODO(jtattermusch): can we make the ports configurable (but avoid breaking bazel build cache at the same time?)
-"${INTEROP_SERVER_BINARY}" --port=5050 --max_send_message_size=8388608 &
-"${INTEROP_SERVER_BINARY}" --port=5051 --max_send_message_size=8388608 --use_tls &
+"${INTEROP_SERVER_BINARY}" --port=$PLAIN_PORT --max_send_message_size=8388608 &
+"${INTEROP_SERVER_BINARY}" --port=$TLS_PORT --max_send_message_size=8388608 --use_tls &
 # make sure the interop_server processes we started on the background are killed upon exit.
 trap 'echo "KILLING interop_server binaries running on the background"; kill -9 $(jobs -p)' EXIT
 # === END SECTION: run interop_server on the background ====
@@ -90,6 +96,8 @@
   --google_credentials="${KOKORO_GFILE_DIR}/GrpcTesting-d0eeee2db331.json" \
   "${BAZEL_REMOTE_CACHE_ARGS[@]}" \
   $BAZEL_FLAGS \
+  --test_env HOST_PORT_LOCAL=localhost:$PLAIN_PORT \
+  --test_env HOST_PORT_LOCALSSL=localhost:$TLS_PORT \
   -- \
   "${EXAMPLE_TARGETS[@]}" \
   "${TEST_TARGETS[@]}"