Browse Source

Update CI to run interoperability tests. (#561)

Motivation:

We don't run the interoperability tests in CI at the moment, we should
run them against a different implementation to help ensure we don't
regress.

Modifications:

- Add two additional CI jobs to run the interoperability tests (which do
  not run the other tests).
- Update the Travis setup so the steps run are in .travis-script.sh
  instead of inline in .travis.yml
- Update .travis-install to conditionally install Bazel from source and
  build the gRPC C++ interop testing servers
- Update the Makefile to make it more configurable (i.e. 'swift' can be
  set to make it easier to run with different toolchains via xcrun)

Result:

- Interop tests will be run in CI in separate build jobs
George Barnett 6 years ago
parent
commit
c5dc8f5d2e

+ 1 - 1
.gitignore

@@ -11,7 +11,7 @@ third_party/**
 /EchoNIO
 /test.out
 /echo.pid
-/SwiftGRPC.xcodeproj
+/*.xcodeproj
 /scripts/tmp/
 Examples/EchoWeb/dist
 Examples/EchoWeb/node_modules

+ 158 - 23
.travis-install.sh

@@ -1,5 +1,5 @@
-#!/bin/sh
-#
+#!/bin/bash -e
+
 # Copyright 2017, gRPC Authors All rights reserved.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
@@ -25,28 +25,163 @@
 # - $HOME/local/lib to LD_LIBRARY_PATH
 #
 
-cd
-mkdir -p local
+# To speed up the CI we cache the interop test server binaries. We cache the
+# binaries with the gRPC version appended to their name as a means of
+# invalidating the cache when we bump versions.
 
+# Update .travis.yml if this changes.
+BIN_CACHE="$HOME"/bin_cache
+
+# Only applies to Linux, we get Swift from Xcode on macOS.
 SWIFT_VERSION=5.0.2
 PROTOBUF_VERSION=3.9.1
+# We need this to build gRPC C++ for the interop test server(s).
+BAZEL_VERSION=0.28.1
+GRPC_VERSION=1.23.0
+
+GREEN='\033[0;32m'
+BLUE='\033[0;34m'
+NO_COLOR='\033[0m'
+
+info() {
+  printf "${BLUE}$1${NO_COLOR}\n"
+}
+
+success() {
+  printf "${GREEN}$1${NO_COLOR}\n"
+}
+
+# Install the protoc compiler.
+install_protoc() {
+  echo -en 'travis_fold:start:install.protoc\\r'
+  info "Installing protoc $PROTOBUF_VERSION"
+
+  # Install protoc
+  if [ "$TRAVIS_OS_NAME" = "osx" ]; then
+    PROTOC_URL=https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-osx-x86_64.zip
+  else
+    PROTOC_URL=https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-linux-x86_64.zip
+  fi
+
+  info "Downloading protoc from: $PROTOC_URL"
+  curl -fSsL $PROTOC_URL -o protoc.zip
+
+  info "Extracting protoc from protoc.zip"
+  unzip -q protoc.zip -d local
+  success "Installed protoc $PROTOBUF_VERSION"
+  echo -en 'travis_fold:end:install.protoc\\r'
+}
+
+# Install Swift.
+install_swift() {
+  echo -en 'travis_fold:start:install.swift\\r'
+
+  # Use the Swift provided by Xcode on macOS.
+  if [ "$TRAVIS_OS_NAME" != "osx" ]; then
+    info "Installing Swift $SWIFT_VERSION"
+
+    SWIFT_URL=https://swift.org/builds/swift-${SWIFT_VERSION}-release/ubuntu1804/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu18.04.tar.gz
+    info "Downloading Swift from $SWIFT_URL"
+    curl -fSsL $SWIFT_URL -o swift.tar.gz
+
+    info "Extracting Swift from swift.tar.gz"
+    tar -xzf swift.tar.gz --strip-components=2 --directory=local
+    success "Installed Swift $SWIFT_VERSION"
+  else
+    info "Skipping Swift installation: using Swift provided by Xcode"
+  fi
+  echo -en 'travis_fold:end:install.swift\\r'
+}
+
+# We need to install bazel to so we can build the gRPC interop test server.
+install_bazel() {
+  echo -en 'travis_fold:start:install.bazel\\r'
+
+  info "Installing Bazel $BAZEL_VERSION"
+
+  # See:
+  # - https://docs.bazel.build/versions/master/install-os-x.html
+  # - https://docs.bazel.build/versions/master/install-ubuntu.html
+  if [ "$TRAVIS_OS_NAME" = "osx" ]; then
+    BAZEL_URL=https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/bazel-${BAZEL_VERSION}-installer-darwin-x86_64.sh
+  else
+    BAZEL_URL=https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/bazel-${BAZEL_VERSION}-installer-linux-x86_64.sh
+  fi
+
+  info "Downloading Bazel from: $BAZEL_URL"
+  curl -fSsL $BAZEL_URL -o bazel-installer.sh
+
+  chmod +x bazel-installer.sh
+  info "Running ./bazel-installer.sh"
+  ./bazel-installer.sh --prefix="$HOME/local"
+  success "Installed Bazel"
+  echo -en 'travis_fold:end:install.bazel\\r'
+}
+
+# Build the gRPC C++ interop test server and reconnect interop test server.
+build_grpc_cpp_server() {
+  echo -en 'travis_fold:start:install.grpc_cpp_server\\r'
+
+  info "Building gRPC $GRPC_VERSION C++ interop servers"
+  GRPC_INTEROP_SERVER=interop_server-"$GRPC_VERSION"
+  GRPC_RECONNECT_INTEROP_SERVER=reconnect_interop_server-"$GRPC_VERSION"
+
+  # If the servers don't exist: download and build them.
+  if [ ! -f "$BIN_CACHE/$GRPC_INTEROP_SERVER" ] || [ ! -f "$BIN_CACHE/$GRPC_RECONNECT_INTEROP_SERVER" ]; then
+    GRPC_URL=https://github.com/grpc/grpc/archive/v${GRPC_VERSION}.tar.gz
+
+    info "Downloading gRPC from: $GRPC_URL"
+    curl -fSsL $GRPC_URL -o grpc.tar.gz
+
+    # Extract it to grpc
+    mkdir grpc
+    info "Extracting grpc.tar.gz to grpc"
+    tar -xzf grpc.tar.gz --strip-components=1 --directory=grpc
+
+    # Build the interop servers and put them in $BIN_CACHE
+    (
+      cd grpc
+      # Only update progress every second to avoid spamming the logs.
+      "$HOME"/local/bin/bazel build \
+        --show_progress_rate_limit=1 \
+        test/cpp/interop:interop_server \
+        test/cpp/interop:reconnect_interop_server
+
+      # Put them in the $BIN_CACHE
+      info "Copying interop server to $BIN_CACHE/$GRPC_INTEROP_SERVER"
+      cp ./bazel-bin/test/cpp/interop/interop_server "$BIN_CACHE/$GRPC_INTEROP_SERVER"
+      info "Copying interop reconnect server to $BIN_CACHE/$GRPC_RECONNECT_INTEROP_SERVER"
+      cp ./bazel-bin/test/cpp/interop/reconnect_interop_server "$BIN_CACHE/$GRPC_RECONNECT_INTEROP_SERVER"
+    )
+  else
+    info "Skipping download and build of gRPC C++, using cached binaries"
+  fi
+
+  # We should have cached servers now, copy them to $HOME/local/bin
+  cp "$BIN_CACHE/$GRPC_INTEROP_SERVER" "$HOME"/local/bin/interop_server
+  cp "$BIN_CACHE/$GRPC_RECONNECT_INTEROP_SERVER" "$HOME"/local/bin/reconnect_interop_server
+
+  success "Copied gRPC interop servers"
+  echo -en 'travis_fold:end:install.grpc_cpp_server\\r'
+}
+
+main() {
+  cd
+  mkdir -p local "$BIN_CACHE"
+
+  install_protoc
+  install_swift
+
+  if [ "$RUN_INTEROP_TESTS" = "true" ]; then
+    install_bazel
+    build_grpc_cpp_server
+  fi
+
+  # Verify installation
+  info "Contents of $HOME/local:"
+  find local
+  success "Install script completed"
+}
 
-if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
-  PROTOC_URL=https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-osx-x86_64.zip
-else
-  # Install swift
-  SWIFT_URL=https://swift.org/builds/swift-${SWIFT_VERSION}-release/ubuntu1804/swift-${SWIFT_VERSION}-RELEASE/swift-${SWIFT_VERSION}-RELEASE-ubuntu18.04.tar.gz
-  echo $SWIFT_URL
-  curl -fSsL $SWIFT_URL -o swift.tar.gz
-  tar -xzf swift.tar.gz --strip-components=2 --directory=local
-
-  PROTOC_URL=https://github.com/google/protobuf/releases/download/v${PROTOBUF_VERSION}/protoc-${PROTOBUF_VERSION}-linux-x86_64.zip
-fi
-
-# Install protoc
-echo $PROTOC_URL
-curl -fSsL $PROTOC_URL -o protoc.zip
-unzip -q protoc.zip -d local
-
-# Verify installation
-find local
+# Run the installation.
+main

+ 180 - 0
.travis-script.sh

@@ -0,0 +1,180 @@
+#!/bin/bash -e
+
+# Copyright 2019, gRPC Authors All rights reserved.
+#
+# 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.
+
+# See: Makefile
+BUILD_OUTPUT=./.build/debug
+
+GREEN='\033[0;32m'
+BLUE='\033[0;34m'
+NO_COLOR='\033[0m'
+
+info() {
+  printf "${BLUE}$1${NO_COLOR}\n"
+}
+
+success() {
+  printf "${GREEN}$1${NO_COLOR}\n"
+}
+
+setup_environment() {
+  echo -en 'travis_fold:start:script.environment\\r'
+  export PATH=$HOME/local/bin:$PATH
+  export LD_LIBRARY_PATH=$HOME/local/lib
+  echo -en 'travis_fold:end:script.environment\\r'
+}
+
+make_all() {
+  echo -en 'travis_fold:start:make.all\\r'
+  info "Running make all"
+  make all
+  success "make all succeeded"
+  echo -en 'travis_fold:end:make.all\\r'
+}
+
+make_test() {
+  echo -en 'travis_fold:start:make.test\\r'
+  info "Running Swift tests"
+  make test
+  success "Swift tests passed"
+  echo -en 'travis_fold:end:make.test\\r'
+}
+
+make_test_plugin() {
+  echo -en 'travis_fold:start:make.test_plugin\\r'
+  info "Validating protoc plugins on the Echo service"
+  make test-plugin
+  success "Validated protoc plugins on the Echo service"
+  echo -en 'travis_fold:end:make.test_plugin\\r'
+}
+
+make_check_linuxmain() {
+  echo -en 'travis_fold:start:make.test_generate_linuxmain\\r'
+  info "Validating LinuxMain is up-to-date"
+  if [ "$TRAVIS_OS_NAME" = "osx" ]; then
+    make test-generate-linuxmain
+    success "LinuxMain is up-to-date"
+  else
+    info "Not running on macOS, skipping LinuxMain validation"
+  fi
+  echo -en 'travis_fold:end:make.test_generate_linuxmain\\r'
+}
+
+make_project() {
+  echo -en 'travis_fold:start:make.project\\r'
+  info "Validating .xcodeproj can be generated"
+  if [ "$TRAVIS_OS_NAME" = "osx" ]; then
+    make project
+    info ".xcodeproj was successfully generated"
+  else
+    info "Not running on macOS, skipping .xcodeproj generation"
+  fi
+  echo -en 'travis_fold:end:make.project\\r'
+}
+
+run_interop_tests() {
+  echo -en 'travis_fold:start:test.interop_tests\\r'
+  make interop-test-runner
+  INTEROP_TEST_SERVER_PORT=8080
+
+  # interop_server should be on $PATH
+  info "Starting C++ interop server on port $INTEROP_TEST_SERVER_PORT"
+  "$HOME"/local/bin/interop_server -port "$INTEROP_TEST_SERVER_PORT" &
+  INTEROP_SERVER_PID=$!
+  success "C++ interop server started, pid=$INTEROP_SERVER_PID"
+
+  # Names of the tests we should run:
+  TESTS=(
+    empty_unary
+    large_unary
+    client_streaming
+    server_streaming
+    ping_pong
+    empty_stream
+    custom_metadata
+    status_code_and_message
+    special_status_message
+    unimplemented_method
+    unimplemented_service
+    cancel_after_begin
+    cancel_after_first_response
+    timeout_on_sleeping_server
+  )
+
+  # Run the tests; logs are written to stderr, capture them per-test.
+  for test in "${TESTS[@]}"; do
+    info "Running $test"
+    $BUILD_OUTPUT/InteroperabilityTestRunner run_test \
+      --test_case "$test" \
+      --server_port $INTEROP_TEST_SERVER_PORT \
+        2> "interop.$test.log"
+    success "PASSED $test"
+  done
+
+  success "Interop tests PASSED"
+  info "Stopping C++ interop server"
+  kill "$INTEROP_SERVER_PID"
+  success "Stopped C++ interop server"
+  echo -en 'travis_fold:end:test.interop_tests\\r'
+}
+
+run_interop_reconnect_test() {
+  echo -en 'travis_fold:start:test.interop_reconnect\\r'
+  make interop-backoff-test-runner
+  INTEROP_TEST_SERVER_CONTROL_PORT=8081
+  INTEROP_TEST_SERVER_RETRY_PORT=8082
+
+  # reconnect_interop_server should be on $PATH
+  info "Starting C++ reconnect interop server:"
+  info " - control port: ${INTEROP_TEST_SERVER_CONTROL_PORT}"
+  info " - retry port: ${INTEROP_TEST_SERVER_RETRY_PORT}"
+  "$HOME"/local/bin/reconnect_interop_server \
+    -control_port "$INTEROP_TEST_SERVER_CONTROL_PORT" \
+    -retry_port "$INTEROP_TEST_SERVER_RETRY_PORT" &
+  INTEROP_RECONNECT_SERVER_PID=$!
+  success "C++ reconnect interop server started, pid=$INTEROP_RECONNECT_SERVER_PID"
+
+  info "Running connection backoff interop test"
+  # Run the test; logs are written to stderr, redirect them to a file.
+  ${BUILD_OUTPUT}/ConnectionBackoffInteropTestRunner \
+    ${INTEROP_TEST_SERVER_CONTROL_PORT} \
+    ${INTEROP_TEST_SERVER_RETRY_PORT} \
+      2> "interop.connection_backoff.log"
+  success "connection backoff interop test PASSED"
+
+  info "Stopping C++ reconnect interop server"
+  kill "$INTEROP_RECONNECT_SERVER_PID"
+  success "Stopped C++ reconnect interop server"
+  echo -en 'travis_fold:end:test.interop_reconnect\\r'
+}
+
+main() {
+  setup_environment
+
+  # If we're running interop tests don't bother with the other stuff.
+  if [ "$RUN_INTEROP_TESTS" = "true" ]; then
+    run_interop_tests
+    run_interop_reconnect_test
+  else
+    make_all
+    make_check_linuxmain
+    make_test
+    make_test_plugin
+    make_project
+  fi
+}
+
+# Run the thing!
+main

+ 43 - 20
.travis.yml

@@ -13,22 +13,48 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 #
-# Travis CI build file for Swift gRPC.
+# Travis CI build file for gRPC Swift.
 
-os:
-  - linux
-  - osx
+jobs:
+  include:
+    # Tests for each PR.
+    - &tests
+      stage: "Test"
+      name: "Ubuntu 18.04"
+      os: linux
+      dist: bionic
+      script: ./.travis-script.sh
+      env: RUN_INTEROP_TESTS=false
+    - <<: *tests
+      name: "macOS (Xcode 10.3)"
+      os: osx
+      osx_image: xcode10.3
+    # Interop Tests.
+    - &interop_tests
+      stage: "Interoperability Tests"
+      # Linux
+      name: "Ubuntu 18.04"
+      os: linux
+      dist: bionic
+      script: ./.travis-script.sh
+      env: RUN_INTEROP_TESTS=true
+    - <<: *interop_tests
+      name: "macOS (Xcode 10.3)"
+      os: osx
+      osx_image: xcode10.3
+
+stages:
+  # Always run this stage.
+  - name: "Test"
+  # Only run when pushing (or merging) to nio.
+  - name: "Interoperability Tests"
+    if: type = push AND branch = nio
 
 cache:
   apt: true
   directories:
-    - .build/checkouts
-    - .build/repositories
-
-# Use Ubuntu 18.04
-dist: bionic
-
-osx_image: xcode10.3
+    - .build
+    - $HOME/bin_cache
 
 sudo: false
 
@@ -38,14 +64,11 @@ addons:
       - build-essential
       - curl
       - unzip
+      - zip
+      - pkg-config
+      - g++
+      - zlib1g-dev
+      - python3
 
 install: ./.travis-install.sh
-
-script:
-  - echo -en 'travis_fold:start:script.environment\\r' && export PATH=$HOME/local/bin:$PATH && export LD_LIBRARY_PATH=$HOME/local/lib && echo -en 'travis_fold:end:script.environment\\r'
-  - echo -en 'travis_fold:start:swift.resolve-deps\\r' && swift package -v resolve && echo -en 'travis_fold:end:swift.resolve-deps\\r'
-  - echo -en 'travis_fold:start:make.all\\r' && make all && echo -en 'travis_fold:end:make.all\\r'
-  - echo -en 'travis_fold:start:make.test_generate_linuxmain\\r' && make test-generate-linuxmain && echo -en 'travis_fold:end:make.test_generate_linuxmain\\r'
-  - echo -en 'travis_fold:start:make.test\\r' && make test && echo -en 'travis_fold:end:make.test\\r'
-  - echo -en 'travis_fold:start:make.test_plugin\\r' && make test-plugin && echo -en 'travis_fold:end:make.test_plugin\\r'
-  - echo -en 'travis_fold:start:make.project\\r' && if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then make project; fi && echo -en 'travis_fold:end:make.project\\r'
+script: ./.travis-script.sh

+ 73 - 26
Makefile

@@ -1,40 +1,87 @@
+# Which Swift to use.
+SWIFT:=swift
+# Where products will be built; this is the SPM default.
+SWIFT_BUILD_PATH:=./.build
+SWIFT_BUILD_CONFIGURATION:=debug
+SWIFT_FLAGS:=--build-path=${SWIFT_BUILD_PATH} --configuration=${SWIFT_BUILD_CONFIGURATION}
+
+SWIFT_BUILD:=${SWIFT} build ${SWIFT_FLAGS}
+SWIFT_TEST:=${SWIFT} test ${SWIFT_FLAGS}
+SWIFT_PACKAGE:=${SWIFT} package ${SWIFT_FLAGS}
+
+# Name of generated xcodeproj
+XCODEPROJ:=GRPC.xcodeproj
+
+### Package and plugin build targets ###########################################
+
 all:
-	swift build
-	cp .build/debug/protoc-gen-swift .
-	cp .build/debug/protoc-gen-swiftgrpc .
+	${SWIFT_BUILD}
+
+plugins: protoc-gen-swift protoc-gen-swiftgrpc
+
+protoc-gen-swift:
+	${SWIFT_BUILD} --product protoc-gen-swift
+
+protoc-gen-swiftgrpc:
+	${SWIFT_BUILD} --product protoc-gen-swiftgrpc
+
+interop-test-runner:
+	${SWIFT_BUILD} --product InteroperabilityTestRunner
 
-plugin:
-	swift build --product protoc-gen-swift -c release
-	swift build --product protoc-gen-swiftgrpc -c release
-	cp .build/release/protoc-gen-swift .
-	cp .build/release/protoc-gen-swiftgrpc .
+interop-backoff-test-runner:
+	${SWIFT_BUILD} --product ConnectionBackoffInteropTestRunner
 
-project:
-	swift package generate-xcodeproj --output GRPC.xcodeproj
-	@-ruby fix-project-settings.rb GRPC.xcodeproj || echo "Consider running 'sudo gem install xcodeproj' to automatically set correct indentation settings for the generated project."
+### Xcodeproj and LinuxMain
 
+project: ${XCODEPROJ}
+
+${XCODEPROJ}:
+	${SWIFT_PACKAGE} generate-xcodeproj --output $@
+	@-ruby fix-project-settings.rb GRPC.xcodeproj || \
+		echo "Consider running 'sudo gem install xcodeproj' to automatically set correct indentation settings for the generated project."
+
+# Generates LinuxMain.swift, only on macOS.
+generate-linuxmain:
+	${SWIFT_TEST} --generate-linuxmain
+
+### Protobuf Generation ########################################################
+
+# Generates protobufs and gRPC client and server for the Echo example
+generate-echo: plugins
+	protoc Sources/Examples/Echo/echo.proto \
+		--proto_path=Sources/Examples/Echo \
+		--plugin=${SWIFT_BUILD_PATH}/${SWIFT_BUILD_CONFIGURATION}/protoc-gen-swift \
+		--plugin=${SWIFT_BUILD_PATH}/${SWIFT_BUILD_CONFIGURATION}/protoc-gen-swiftgrpc \
+		--swift_out=Sources/Examples/Echo/Generated \
+		--swiftgrpc_out=Sources/Examples/Echo/Generated
+
+### Testing ####################################################################
+
+# Normal test suite.
 test:
-	swift test
+	${SWIFT_TEST}
+
+# Checks that linuxmain has been updated: requires macOS.
+test-generate-linuxmain: generate-linuxmain
+	@git diff --exit-code Tests/LinuxMain.swift Tests/*/XCTestManifests.swift > /dev/null || \
+		{ echo "Generated tests are out-of-date; run 'swift test --generate-linuxmain' to update them!"; exit 1; }
 
-test-plugin:
-	swift build --product protoc-gen-swiftgrpc
-	protoc Sources/Examples/Echo/echo.proto --proto_path=Sources/Examples/Echo --plugin=.build/debug/protoc-gen-swift --plugin=.build/debug/protoc-gen-swiftgrpc --swiftgrpc_out=/tmp
+# Generates code for the Echo server and client and tests them against 'golden' data.
+test-plugin: plugins
+	protoc Sources/Examples/Echo/echo.proto \
+		--proto_path=Sources/Examples/Echo \
+		--plugin=${SWIFT_BUILD_PATH}/${SWIFT_BUILD_CONFIGURATION}/protoc-gen-swift \
+		--plugin=${SWIFT_BUILD_PATH}/${SWIFT_BUILD_CONFIGURATION}/protoc-gen-swiftgrpc \
+		--swiftgrpc_out=/tmp
 	diff -u /tmp/echo.grpc.swift Sources/Examples/Echo/Generated/echo.grpc.swift
 
-test-generate-linuxmain:
-ifeq ($(UNAME_S), Darwin)
-	swift test --generate-linuxmain
-	@git diff --exit-code */LinuxMain.swift */XCTestManifests.swift > /dev/null || { echo "Generated tests are out-of-date; run 'swift test --generate-linuxmain' to update them!"; exit 1; }
-else
-	echo "test-generate-linuxmain is only available on Darwin"
-endif
+### Misc. ######################################################################
 
 clean:
 	-rm -rf Packages
-	-rm -rf .build build
-	-rm -rf GRPC.xcodeproj
-	-rm -rf Package.pins Package.resolved
-	-rm -rf protoc-gen-swift protoc-gen-swiftgrpc
+	-rm -rf ${SWIFT_BUILD_PATH}
+	-rm -rf ${XCODEPROJ}
+	-rm -f Package.pins Package.resolved
 	-cd Examples/Google/Datastore && make clean
 	-cd Examples/Google/NaturalLanguage && make clean
 	-cd Examples/Google/Spanner && make clean

+ 2 - 2
Package.resolved

@@ -24,8 +24,8 @@
         "repositoryURL": "https://github.com/apple/swift-log",
         "state": {
           "branch": null,
-          "revision": "f4240bf022a69815241a883c03645444b58ac553",
-          "version": "1.1.0"
+          "revision": "e8aabbe95db22e064ad42f1a4a9f8982664c70ed",
+          "version": "1.1.1"
         }
       },
       {

+ 3 - 1
Package.swift

@@ -105,7 +105,8 @@ let package = Package(
       name: "GRPCInteroperabilityTests",
       dependencies: [
         "GRPCInteroperabilityTestsImplementation",
-        "Commander"
+        "Commander",
+        "Logging",
       ]
     ),
 
@@ -115,6 +116,7 @@ let package = Package(
       dependencies: [
         "GRPC",
         "GRPCInteroperabilityTestModels",
+        "Logging",
       ]
     ),
 

+ 20 - 9
Sources/GRPCConnectionBackoffInteropTest/main.swift

@@ -30,10 +30,18 @@ guard args.count == 3, let controlPort = Int(args[1]), let retryPort = Int(args[
 
 // MARK: - Setup
 
+// Since this is a long running test, print connectivity state changes to stdout with timestamps.
+// We'll redirect logs to stderr so that stdout contains information only relevant to the test.
+class PrintingConnectivityStateDelegate: ConnectivityStateDelegate {
+  func connectivityStateDidChange(from oldState: ConnectivityState, to newState: ConnectivityState) {
+    print("[\(Date())] connectivity state change: \(oldState) → \(newState)")
+  }
+}
+
 // Reduce stdout noise.
 LoggingSystem.bootstrap(StreamLogHandler.standardError)
 
-let group = MultiThreadedEventLoopGroup(numberOfThreads: 2)
+let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
 defer {
   try! group.syncShutdownGracefully()
 }
@@ -49,28 +57,29 @@ let controlConfig = ClientConnection.Configuration(
 let retryConfig = ClientConnection.Configuration(
   target: .hostAndPort("localhost", retryPort),
   eventLoopGroup: group,
+  connectivityStateDelegate: PrintingConnectivityStateDelegate(),
   tls: .init(),
   connectionBackoff: .init()
 )
 
 // MARK: - Test Procedure
 
-print("Starting connection backoff interoperability test...")
+print("[\(Date())] Starting connection backoff interoperability test...")
 
 // 1. Call 'Start' on server control port with a large deadline or no deadline, wait for it to
 //    finish and check it succeeded.
 let controlConnection = ClientConnection(configuration: controlConfig)
 let controlClient = Grpc_Testing_ReconnectServiceServiceClient(connection: controlConnection)
-print("Control 'Start' call started")
+print("[\(Date())] Control 'Start' call started")
 let controlStart = controlClient.start(.init(), callOptions: .init(timeout: .infinite))
 let controlStartStatus = try controlStart.status.wait()
 assert(controlStartStatus.code == .ok, "Control Start rpc failed: \(controlStartStatus.code)")
-print("Control 'Start' call succeeded")
+print("[\(Date())] Control 'Start' call succeeded")
 
 // 2. Initiate a channel connection to server retry port, which should perform reconnections with
 //    proper backoffs. A convenient way to achieve this is to call 'Start' with a deadline of 540s.
 //    The rpc should fail with deadline exceeded.
-print("Retry 'Start' call started")
+print("[\(Date())] Retry 'Start' call started")
 let retryConnection = ClientConnection(configuration: retryConfig)
 let retryClient = Grpc_Testing_ReconnectServiceServiceClient(
   connection: retryConnection,
@@ -81,19 +90,19 @@ let retryStart = retryClient.start(.init())
 let retryStartStatus = try retryStart.status.wait()
 assert(retryStartStatus.code == .deadlineExceeded,
        "Retry Start rpc status was not 'deadlineExceeded': \(retryStartStatus.code)")
-print("Retry 'Start' call terminated with expected status")
+print("[\(Date())] Retry 'Start' call terminated with expected status")
 
 // 3. Call 'Stop' on server control port and check it succeeded.
-print("Control 'Stop' call started")
+print("[\(Date())] Control 'Stop' call started")
 let controlStop = controlClient.stop(.init())
 let controlStopStatus = try controlStop.status.wait()
 assert(controlStopStatus.code == .ok, "Control Stop rpc failed: \(controlStopStatus.code)")
-print("Control 'Stop' call succeeded")
+print("[\(Date())] Control 'Stop' call succeeded")
 
 // 4. Check the response to see whether the server thinks the backoffs passed the test.
 let controlResponse = try controlStop.response.wait()
 assert(controlResponse.passed, "TEST FAILED")
-print("TEST PASSED")
+print("[\(Date())] TEST PASSED")
 
 // MARK: - Tear down
 
@@ -101,5 +110,7 @@ print("TEST PASSED")
 
 // We expect close to fail on the retry connection because the channel should never be successfully
 // started.
+print("[\(Date())] Closing Retry connection")
 try? retryConnection.close().wait()
+print("[\(Date())] Closing Control connection")
 try controlConnection.close().wait()

+ 2 - 2
Sources/GRPCInteroperabilityTestModels/generate.sh

@@ -1,7 +1,7 @@
 #!/bin/sh
 
-PLUGIN_SWIFT=../../protoc-gen-swift
-PLUGIN_SWIFTGRPC=../../protoc-gen-swiftgrpc
+PLUGIN_SWIFT=../../.build/debug/protoc-gen-swift
+PLUGIN_SWIFTGRPC=../../.build/debug/protoc-gen-swiftgrpc
 PROTO="src/proto/grpc/testing/test.proto"
 
 OUTPUT="Generated"

+ 4 - 0
Sources/GRPCInteroperabilityTests/main.swift

@@ -19,6 +19,10 @@ import NIO
 import NIOSSL
 import GRPCInteroperabilityTestsImplementation
 import Commander
+import Logging
+
+// Reduce stdout noise.
+LoggingSystem.bootstrap(StreamLogHandler.standardError)
 
 enum InteroperabilityTestError: LocalizedError {
   case testNotFound(String)

+ 10 - 0
Tests/GRPCTests/XCTestManifests.swift

@@ -450,6 +450,15 @@ extension ServerWebTests {
     ]
 }
 
+extension StopwatchTests {
+    // DO NOT MODIFY: This is autogenerated, use:
+    //   `swift test --generate-linuxmain`
+    // to regenerate.
+    static let __allTests__StopwatchTests = [
+        ("testElapsed", testElapsed),
+    ]
+}
+
 extension StreamingRequestClientCallTests {
     // DO NOT MODIFY: This is autogenerated, use:
     //   `swift test --generate-linuxmain`
@@ -491,6 +500,7 @@ public func __allTests() -> [XCTestCaseEntry] {
         testCase(ServerErrorTransformingTests.__allTests__ServerErrorTransformingTests),
         testCase(ServerThrowingTests.__allTests__ServerThrowingTests),
         testCase(ServerWebTests.__allTests__ServerWebTests),
+        testCase(StopwatchTests.__allTests__StopwatchTests),
         testCase(StreamingRequestClientCallTests.__allTests__StreamingRequestClientCallTests),
     ]
 }