Browse Source

Integration tests for the build plugin (#31)

### Motivation:

To protect against regressions in common use-cases of the
grpc-swift-protobuf build plugin.

### Modifications:

Add test cases which make use of the build plugin as a dependency and
ensure that they can compile and use the generated code
* top level config file
* peer config file
* separate service message protos
* cross directory imports
* two definitions
* nested definitions

The new tests are run as part of CI on PRs

### Result:

More CI.

---------

Co-authored-by: George Barnett <gbarnett@apple.com>
Rick Newton-Rogers 10 months ago
parent
commit
4153adaff5

+ 26 - 0
.github/workflows/main.yml

@@ -16,3 +16,29 @@ jobs:
       linux_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
       linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
       linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
+
+  construct-plugin-tests-matrix:
+    name: Construct plugin tests matrix
+    runs-on: ubuntu-latest
+    outputs:
+      plugin-tests-matrix: '${{ steps.generate-matrix.outputs.plugin-tests-matrix }}'
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+        with:
+          persist-credentials: false
+      - id: generate-matrix
+        run: echo "plugin-tests-matrix=$(curl -s https://raw.githubusercontent.com/apple/swift-nio/main/scripts/generate_matrix.sh | bash)" >> "$GITHUB_OUTPUT"
+        env:
+          MATRIX_LINUX_5_9_ENABLED: false
+          MATRIX_LINUX_5_10_ENABLED: false
+          MATRIX_LINUX_COMMAND: "curl -s https://raw.githubusercontent.com/rnro/grpc-swift-protobuf/refs/heads/build_plugin_integration_tests/dev/plugin-tests.sh | GITHUB_ACTIONS=true bash"
+          MATRIX_LINUX_SETUP_COMMAND: "apt-get update -y -q && apt-get install -y -q curl protobuf-compiler"
+
+  plugin-tests-matrix:
+    name: Plugin tests
+    needs: construct-plugin-tests-matrix
+    uses: apple/swift-nio/.github/workflows/swift_test_matrix.yml@main
+    with:
+      name: "Plugin tests"
+      matrix_string: '${{ needs.construct-plugin-tests-matrix.outputs.plugin-tests-matrix }}'

+ 26 - 0
.github/workflows/pull_request.yml

@@ -26,6 +26,32 @@ jobs:
       linux_nightly_6_0_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
       linux_nightly_main_arguments_override: "--explicit-target-dependency-import-check error -Xswiftc -require-explicit-sendable"
 
+  construct-plugin-tests-matrix:
+    name: Construct plugin tests matrix
+    runs-on: ubuntu-latest
+    outputs:
+      plugin-tests-matrix: '${{ steps.generate-matrix.outputs.plugin-tests-matrix }}'
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+        with:
+          persist-credentials: false
+      - id: generate-matrix
+        run: echo "plugin-tests-matrix=$(curl -s https://raw.githubusercontent.com/apple/swift-nio/main/scripts/generate_matrix.sh | bash)" >> "$GITHUB_OUTPUT"
+        env:
+          MATRIX_LINUX_5_9_ENABLED: false
+          MATRIX_LINUX_5_10_ENABLED: false
+          MATRIX_LINUX_COMMAND: "curl -s https://raw.githubusercontent.com/rnro/grpc-swift-protobuf/refs/heads/build_plugin_integration_tests/dev/plugin-tests.sh | GITHUB_ACTIONS=true bash"
+          MATRIX_LINUX_SETUP_COMMAND: "apt-get update -y -q && apt-get install -y -q curl protobuf-compiler"
+
+  plugin-tests-matrix:
+    name: Plugin tests
+    needs: construct-plugin-tests-matrix
+    uses: apple/swift-nio/.github/workflows/swift_test_matrix.yml@main
+    with:
+      name: "Plugin tests"
+      matrix_string: '${{ needs.construct-plugin-tests-matrix.outputs.plugin-tests-matrix }}'
+
   cxx-interop:
     name: Cxx interop
     uses: apple/swift-nio/.github/workflows/cxx_interop.yml@main

+ 1 - 0
.licenseignore

@@ -36,6 +36,7 @@ dev/git.commit.template
 dev/version-bump.commit.template
 dev/protos/local/*
 dev/protos/upstream/*
+IntegrationTests/PluginTests/**/*.proto
 .unacceptablelanguageignore
 LICENSE
 **/*.swift

+ 7 - 0
IntegrationTests/PluginTests/Resources/Foo/foo-messages.proto

@@ -0,0 +1,7 @@
+// Leading trivia.
+syntax = "proto3";
+
+package foo;
+
+message FooInput {}
+message FooOutput {}

+ 13 - 0
IntegrationTests/PluginTests/Resources/Foo/foo-service.proto

@@ -0,0 +1,13 @@
+syntax = "proto3";
+
+import "Foo/foo-messages.proto";
+
+package foo;
+
+service FooService1 {
+  rpc Foo (FooInput) returns (FooOutput) {}
+}
+
+service FooService2 {
+  rpc Foo (FooInput) returns (FooOutput) {}
+}

+ 74 - 0
IntegrationTests/PluginTests/Resources/FooHelloWorldAdopter.swift

@@ -0,0 +1,74 @@
+/*
+ * Copyright 2024, 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.
+ */
+
+import GRPCCore
+import GRPCInProcessTransport
+import GRPCProtobuf
+
+@main
+struct PluginAdopter {
+  static func main() async throws {
+    let inProcess = InProcessTransport()
+    try await withGRPCServer(transport: inProcess.server, services: [Greeter()]) { server in
+      try await withGRPCClient(transport: inProcess.client) { client in
+        try await Self.doRPC(Helloworld_Greeter.Client(wrapping: client))
+      }
+    }
+
+    try await withGRPCServer(transport: inProcess.server, services: [FooService1()]) { server in
+      try await withGRPCClient(transport: inProcess.client) { client in
+        try await Self.doRPC(Foo_FooService1.Client(wrapping: client))
+      }
+    }
+  }
+
+  static func doRPC<Transport>(_ greeter: Helloworld_Greeter.Client<Transport>) async throws {
+    do {
+      let reply = try await greeter.sayHello(.with { $0.name = "(ignored)" })
+      print("Reply: \(reply.message)")
+    } catch {
+      print("Error: \(error)")
+    }
+  }
+
+  static func doRPC<Transport>(_ fooService1: Foo_FooService1.Client<Transport>) async throws {
+    do {
+      let reply = try await fooService1.foo(.with { _ in () })
+      print("Reply: \(reply.hashValue)")
+    } catch {
+      print("Error: \(error)")
+    }
+  }
+}
+
+struct Greeter: Helloworld_Greeter.SimpleServiceProtocol {
+  func sayHello(
+    request: Helloworld_HelloRequest,
+    context: ServerContext
+  ) async throws -> Helloworld_HelloReply {
+    return .with { reply in
+      reply.message = "Hello, world!"
+    }
+  }
+}
+
+struct FooService1: Foo_FooService1.SimpleServiceProtocol {
+  func foo(request: Foo_FooInput, context: GRPCCore.ServerContext) async throws -> Foo_FooOutput {
+    return .with { _ in
+      ()
+    }
+  }
+}

+ 37 - 0
IntegrationTests/PluginTests/Resources/HelloWorld/HelloWorld.proto

@@ -0,0 +1,37 @@
+// Copyright 2015, 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.
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.grpc.examples.helloworld";
+option java_outer_classname = "HelloWorldProto";
+option objc_class_prefix = "HLW";
+
+package helloworld;
+
+// The greeting service definition.
+service Greeter {
+  // Sends a greeting
+  rpc SayHello (HelloRequest) returns (HelloReply) {}
+}
+
+// The request message containing the user's name.
+message HelloRequest {
+  string name = 1;
+}
+
+// The response message containing the greetings
+message HelloReply {
+  string message = 1;
+}

+ 31 - 0
IntegrationTests/PluginTests/Resources/HelloWorld/Messages.proto

@@ -0,0 +1,31 @@
+// Copyright 2015, 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.
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.grpc.examples.helloworld";
+option java_outer_classname = "HelloWorldProto";
+option objc_class_prefix = "HLW";
+
+package helloworld;
+
+// The request message containing the user's name.
+message HelloRequest {
+  string name = 1;
+}
+
+// The response message containing the greetings
+message HelloReply {
+  string message = 1;
+}

+ 29 - 0
IntegrationTests/PluginTests/Resources/HelloWorld/Service.proto

@@ -0,0 +1,29 @@
+// Copyright 2015, 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.
+syntax = "proto3";
+
+option java_multiple_files = true;
+option java_package = "io.grpc.examples.helloworld";
+option java_outer_classname = "HelloWorldProto";
+option objc_class_prefix = "HLW";
+
+package helloworld;
+
+import "Messages.proto";
+
+// The greeting service definition.
+service Greeter {
+  // Sends a greeting
+  rpc SayHello (HelloRequest) returns (HelloReply) {}
+}

+ 51 - 0
IntegrationTests/PluginTests/Resources/HelloWorldAdopter.swift

@@ -0,0 +1,51 @@
+/*
+ * Copyright 2024, 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.
+ */
+
+import GRPCCore
+import GRPCInProcessTransport
+import GRPCProtobuf
+
+@main
+struct PluginAdopter {
+  static func main() async throws {
+    let inProcess = InProcessTransport()
+    try await withGRPCServer(transport: inProcess.server, services: [Greeter()]) { server in
+      try await withGRPCClient(transport: inProcess.client) { client in
+        try await Self.doRPC(Helloworld_Greeter.Client(wrapping: client))
+      }
+    }
+  }
+
+  static func doRPC<Transport>(_ greeter: Helloworld_Greeter.Client<Transport>) async throws {
+    do {
+      let reply = try await greeter.sayHello(.with { $0.name = "(ignored)" })
+      print("Reply: \(reply.message)")
+    } catch {
+      print("Error: \(error)")
+    }
+  }
+}
+
+struct Greeter: Helloworld_Greeter.SimpleServiceProtocol {
+  func sayHello(
+    request: Helloworld_HelloRequest,
+    context: ServerContext
+  ) async throws -> Helloworld_HelloReply {
+    return .with { reply in
+      reply.message = "Hello, world!"
+    }
+  }
+}

+ 50 - 0
IntegrationTests/PluginTests/Resources/Package.swift

@@ -0,0 +1,50 @@
+// swift-tools-version: 6.0
+/*
+ * Copyright 2024, 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.
+ */
+
+import PackageDescription
+
+let package = Package(
+  name: "grpc-adopter",
+  platforms: [
+    .macOS(.v15),
+    .iOS(.v18),
+    .tvOS(.v18),
+    .watchOS(.v11),
+    .visionOS(.v2),
+  ],
+  dependencies: [
+    // Dependency on grpc-swift-protobuf to be added by setup-plugin-tests.sh script
+
+    .package(
+      url: "https://github.com/grpc/grpc-swift.git",
+      branch: "main"
+    )
+  ],
+  targets: [
+    .executableTarget(
+      name: "grpc-adopter",
+      dependencies: [
+        .product(name: "GRPCCore", package: "grpc-swift"),
+        .product(name: "GRPCInProcessTransport", package: "grpc-swift"),
+        .product(name: "GRPCProtobuf", package: "grpc-swift-protobuf"),
+      ],
+      plugins: [
+        .plugin(name: "GRPCProtobufGenerator", package: "grpc-swift-protobuf")
+      ]
+    )
+  ]
+)

+ 11 - 0
IntegrationTests/PluginTests/Resources/import-directory-1-grpc-swift-proto-generator-config.json

@@ -0,0 +1,11 @@
+{
+  "generatedSource": {
+    "accessLevel": "internal"
+  },
+  "protoc": {
+    "importPaths": [
+      ".",
+      "../directory_1"
+    ]
+  }
+}

+ 5 - 0
IntegrationTests/PluginTests/Resources/internal-grpc-swift-proto-generator-config.json

@@ -0,0 +1,5 @@
+{
+  "generatedSource": {
+    "accessLevel": "internal"
+  }
+}

+ 6 - 0
IntegrationTests/PluginTests/Resources/public-grpc-swift-proto-generator-config.json

@@ -0,0 +1,6 @@
+{
+  "generatedSource": {
+    "accessLevel": "public"
+  }
+}
+

+ 40 - 0
dev/execute-plugin-tests.sh

@@ -0,0 +1,40 @@
+#!/bin/bash
+## Copyright 2024, 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.
+
+set -euo pipefail
+
+log() { printf -- "** %s\n" "$*" >&2; }
+error() { printf -- "** ERROR: %s\n" "$*" >&2; }
+fatal() { error "$@"; exit 1; }
+
+tests_directory="${PLUGIN_TESTS_DIRECTORY:=""}"
+if [[ -z "$tests_directory" ]]; then
+  fatal "Tests parent directory must be specified."
+fi
+
+for dir in "$tests_directory"/test_*/ ; do
+  if [[ -f "$dir/Package.swift" ]]; then
+    plugin_test=$(basename "$dir")
+    log "Building '$plugin_test' plugin test"
+
+    if ! build_output=$(swift build --package-path "$dir" 2>&1); then
+      # Only print the build output on failure.
+      echo "$build_output"
+      fatal "Build failed for '$plugin_test'"
+    else
+      log "Build succeeded for '$plugin_test'"
+    fi
+  fi
+done

+ 33 - 0
dev/plugin-tests.sh

@@ -0,0 +1,33 @@
+#!/bin/bash
+## Copyright 2024, 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.
+
+set -euo pipefail
+
+log() { printf -- "** %s\n" "$*" >&2; }
+error() { printf -- "** ERROR: %s\n" "$*" >&2; }
+fatal() { error "$@"; exit 1; }
+
+if [[ -n ${GITHUB_ACTIONS:=""} ]]; then
+    # we will have been piped to bash and won't know the location of the script
+    echo "Running in GitHub Actions"
+    source_directory="$(pwd)"
+else
+    here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+    source_directory="$(readlink -f "${here}/..")"
+fi
+tests_directory="${PLUGIN_TESTS_OUTPUT_DIRECTORY:=$(mktemp -d)}"
+
+PLUGIN_TESTS_OUTPUT_DIRECTORY="$tests_directory" "${source_directory}/dev/setup-plugin-tests.sh"
+PLUGIN_TESTS_DIRECTORY="$tests_directory" "${source_directory}/dev/execute-plugin-tests.sh"

+ 96 - 0
dev/setup-plugin-tests.sh

@@ -0,0 +1,96 @@
+#!/bin/bash
+## Copyright 2024, 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.
+
+set -euo pipefail
+
+log() { printf -- "** %s\n" "$*" >&2; }
+error() { printf -- "** ERROR: %s\n" "$*" >&2; }
+fatal() { error "$@"; exit 1; }
+
+output_directory="${PLUGIN_TESTS_OUTPUT_DIRECTORY:=$(mktemp -d)}"
+
+here="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
+grpc_swift_protobuf_directory="$(readlink -f "${here}/..")"
+resources_directory="$(readlink -f "${grpc_swift_protobuf_directory}/IntegrationTests/PluginTests/Resources")"
+scratch_directory="$(mktemp -d)"
+
+echo "Output directory: $output_directory"
+echo "grpc-swift-protobuf directory: $grpc_swift_protobuf_directory"
+
+# modify Package.swift
+cp "${resources_directory}/Package.swift" "${scratch_directory}/"
+cat >> "${scratch_directory}/Package.swift" <<- EOM
+package.dependencies.append(
+  .package(path: "$grpc_swift_protobuf_directory")
+)
+EOM
+
+# test_01_top_level_config_file
+test_01_output_directory="${output_directory}/test_01_top_level_config_file"
+mkdir -p "${test_01_output_directory}/Sources/Protos"
+cp "${scratch_directory}/Package.swift" "${test_01_output_directory}/"
+cp "${resources_directory}/HelloWorldAdopter.swift" "${test_01_output_directory}/Sources/adopter.swift"
+cp "${resources_directory}/HelloWorld/HelloWorld.proto" "${test_01_output_directory}/Sources/Protos"
+cp "${resources_directory}/internal-grpc-swift-proto-generator-config.json" "${test_01_output_directory}/Sources/grpc-swift-proto-generator-config.json"
+
+# test_02_peer_config_file
+test_02_output_directory="${output_directory}/test_02_peer_config_file"
+mkdir -p "${test_02_output_directory}/Sources/Protos"
+cp "${scratch_directory}/Package.swift" "${test_02_output_directory}/"
+cp "${resources_directory}/HelloWorldAdopter.swift" "${test_02_output_directory}/Sources/adopter.swift"
+cp "${resources_directory}/HelloWorld/HelloWorld.proto" "${test_02_output_directory}/Sources/Protos/"
+cp "${resources_directory}/internal-grpc-swift-proto-generator-config.json" "${test_02_output_directory}/Sources/Protos/grpc-swift-proto-generator-config.json"
+
+# test_03_separate_service_message_protos
+test_03_output_directory="${output_directory}/test_03_separate_service_message_protos"
+mkdir -p "${test_03_output_directory}/Sources/Protos"
+cp "${scratch_directory}/Package.swift" "${test_03_output_directory}/"
+cp "${resources_directory}/HelloWorldAdopter.swift" "${test_03_output_directory}/Sources/adopter.swift"
+cp "${resources_directory}/internal-grpc-swift-proto-generator-config.json" "${test_03_output_directory}/Sources/Protos/grpc-swift-proto-generator-config.json"
+cp "${resources_directory}/HelloWorld/Service.proto"  "${test_03_output_directory}/Sources/Protos/"
+cp "${resources_directory}/HelloWorld/Messages.proto"  "${test_03_output_directory}/Sources/Protos/"
+
+# test_04_cross_directory_imports
+test_04_output_directory="${output_directory}/test_04_cross_directory_imports"
+mkdir -p "${test_04_output_directory}/Sources/Protos/directory_1"
+mkdir -p "${test_04_output_directory}/Sources/Protos/directory_2"
+cp "${scratch_directory}/Package.swift" "${test_04_output_directory}/"
+cp "${resources_directory}/HelloWorldAdopter.swift" "${test_04_output_directory}/Sources/adopter.swift"
+cp "${resources_directory}/internal-grpc-swift-proto-generator-config.json" "${test_04_output_directory}/Sources/Protos/directory_1/grpc-swift-proto-generator-config.json"
+cp "${resources_directory}/import-directory-1-grpc-swift-proto-generator-config.json" "${test_04_output_directory}/Sources/Protos/directory_2/grpc-swift-proto-generator-config.json"
+cp "${resources_directory}/HelloWorld/Service.proto" "${test_04_output_directory}/Sources/Protos/directory_2/"
+cp "${resources_directory}/HelloWorld/Messages.proto" "${test_04_output_directory}/Sources/Protos/directory_1/"
+
+# test_05_two_definitions
+test_05_output_directory="${output_directory}/test_05_two_definitions"
+mkdir -p "${test_05_output_directory}/Sources/Protos/HelloWorld"
+mkdir -p "${test_05_output_directory}/Sources/Protos/Foo"
+cp "${scratch_directory}/Package.swift" "${test_05_output_directory}/"
+cp "${resources_directory}/FooHelloWorldAdopter.swift" "${test_05_output_directory}/Sources/adopter.swift"
+cp "${resources_directory}/HelloWorld/HelloWorld.proto" "${test_05_output_directory}/Sources/Protos/HelloWorld/"
+cp "${resources_directory}/internal-grpc-swift-proto-generator-config.json" "${test_05_output_directory}/Sources/Protos/grpc-swift-proto-generator-config.json"
+cp "${resources_directory}/Foo/foo-messages.proto" "${test_05_output_directory}/Sources/Protos/Foo/"
+cp "${resources_directory}/Foo/foo-service.proto" "${test_05_output_directory}/Sources/Protos/Foo/"
+
+# test_06_nested_definitions
+test_06_output_directory="${output_directory}/test_06_nested_definitions"
+mkdir -p "${test_06_output_directory}/Sources/Protos/HelloWorld/FooDefinitions/Foo"
+cp "${scratch_directory}/Package.swift" "${test_06_output_directory}/"
+cp "${resources_directory}/FooHelloWorldAdopter.swift" "${test_06_output_directory}/Sources/adopter.swift"
+cp "${resources_directory}/HelloWorld/HelloWorld.proto" "${test_06_output_directory}/Sources/Protos/HelloWorld/"
+cp "${resources_directory}/internal-grpc-swift-proto-generator-config.json" "${test_06_output_directory}/Sources/Protos/HelloWorld/grpc-swift-proto-generator-config.json"
+cp "${resources_directory}/public-grpc-swift-proto-generator-config.json" "${test_06_output_directory}/Sources/Protos/HelloWorld/FooDefinitions/grpc-swift-proto-generator-config.json"
+cp "${resources_directory}/Foo/foo-messages.proto" "${test_06_output_directory}/Sources/Protos/HelloWorld/FooDefinitions/Foo/"
+cp "${resources_directory}/Foo/foo-service.proto" "${test_06_output_directory}/Sources/Protos/HelloWorld/FooDefinitions/Foo/"