From 0eaf7f2a10528f92139dfdc20f98b1fc683ebf9e Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Fri, 29 Mar 2024 14:27:02 +0000 Subject: [PATCH 01/14] chore: add integration test running in docker container --- .../cloudbuild-test-library-generation.yaml | 31 ---- .../test/container_integration_tests.py | 160 ++++++++++++++++++ .../test/container_integration_tests.sh | 49 ------ 3 files changed, 160 insertions(+), 80 deletions(-) delete mode 100644 .cloudbuild/library_generation/cloudbuild-test-library-generation.yaml create mode 100644 library_generation/test/container_integration_tests.py delete mode 100644 library_generation/test/container_integration_tests.sh diff --git a/.cloudbuild/library_generation/cloudbuild-test-library-generation.yaml b/.cloudbuild/library_generation/cloudbuild-test-library-generation.yaml deleted file mode 100644 index 6cdb333f19..0000000000 --- a/.cloudbuild/library_generation/cloudbuild-test-library-generation.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2024 Google LLC -# -# 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. - -timeout: 7200s # 2 hours -substitutions: - _TEST_IMAGE_ID: 'gcr.io/cloud-devrel-public-resources/java-library-generation:${COMMIT_SHA}' - -steps: - # Library generation build - - name: gcr.io/cloud-builders/docker - args: ["build", "-t", "${_TEST_IMAGE_ID}", "--file", ".cloudbuild/library_generation/library_generation.Dockerfile", "."] - id: library-generation-build - waitFor: ["-"] - - name: ${_TEST_IMAGE_ID} - entrypoint: bash - args: [ './library_generation/test/container_integration_tests.sh' ] - waitFor: [ "library-generation-build" ] - env: - - 'TEST_IMAGE_ID=${_TEST_IMAGE_ID}' - diff --git a/library_generation/test/container_integration_tests.py b/library_generation/test/container_integration_tests.py new file mode 100644 index 0000000000..9b83b304b7 --- /dev/null +++ b/library_generation/test/container_integration_tests.py @@ -0,0 +1,160 @@ +# Copyright 2024 Google LLC +# +# 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 +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +from git import Repo +import os +import shutil +import subprocess +import unittest +from distutils.dir_util import copy_tree +from distutils.file_util import copy_file +import uuid +from pathlib import Path +from library_generation.model.generation_config import GenerationConfig +from library_generation.model.generation_config import from_yaml +from library_generation.utilities import sh_util as shell_call + +script_dir = os.path.dirname(os.path.realpath(__file__)) +golden_dir = os.path.join(script_dir, "resources", "integration", "golden") +repo_root_dir = os.path.join(script_dir, "..", "..") +docker_file = os.path.join( + repo_root_dir, ".cloudbuild", "library_generation", "library_generation.Dockerfile" +) +image_tag = f"test-{str(uuid.uuid4())[:7]}" +repo_prefix = "https://github.com/googleapis" +output_dir = shell_call("get_output_folder") +# this map tells which branch of each repo should we use for our diff tests +committish_map = { + "google-cloud-java": "chore/test-hermetic-build", + "java-bigtable": "chore/test-hermetic-build", +} +config_dir = f"{script_dir}/resources/integration" +config_name = "generation_config.yaml" + + +class ContainerIntegrationTest(unittest.TestCase): + def test_entry_point_running_in_container(self): + # build docker image + subprocess.check_call( + ["docker", "build", "--rm", "-f", docker_file, "-t", image_tag, "."], + cwd=repo_root_dir, + ) + + shutil.rmtree(f"{golden_dir}", ignore_errors=True) + os.makedirs(f"{golden_dir}", exist_ok=True) + config_files = self.__get_config_files(config_dir) + for repo, config_file in config_files: + config = from_yaml(config_file) + repo_dest = self.__pull_repo_to( + Path(f"{output_dir}/{repo}"), repo, committish_map[repo] + ) + library_names = self.__get_library_names_from_config(config) + # prepare golden files + for library_name in library_names: + if config.is_monorepo: + copy_tree( + f"{repo_dest}/{library_name}", f"{golden_dir}/{library_name}" + ) + copy_tree( + f"{repo_dest}/gapic-libraries-bom", + f"{golden_dir}/gapic-libraries-bom", + ) + copy_file(f"{repo_dest}/pom.xml", golden_dir) + else: + copy_tree(f"{repo_dest}", f"{golden_dir}/{library_name}") + # bind repository to docker volumes + self.__bind_device_to_volumes( + volume_name=f"repo-{repo}", device_dir=f"{output_dir}/{repo}" + ) + # bind configuration to docker volumes + self.__bind_device_to_volumes( + volume_name=f"config-{repo}", device_dir=f"{golden_dir}/../{repo}" + ) + repo_volumes = f"-v repo-{repo}:/workspace/{repo} -v config-{repo}:/workspace/config-{repo}" + # run docker container + subprocess.check_call( + [ + "docker", + "run", + "--rm", + "-v", + f"repo-{repo}:/workspace/{repo}", + "-v", + f"config-{repo}:/workspace/config-{repo}", + "-v", + "/tmp:/tmp", + "-v", + "/var/run/docker.sock:/var/run/docker.sock", + "-e", + "RUNNING_IN_DOCKER=true", + "-e", + f"REPO_BINDING_VOLUMES={repo_volumes}", + "-w", + "/src", + image_tag, + "python", + "/src/generate_repo.py", + "generate", + f"--generation-config-yaml=/workspace/config-{repo}/{config_name}", + f"--repository-path=/workspace/{repo}", + ] + ) + + @classmethod + def __pull_repo_to(cls, dest: Path, repo: str, committish: str) -> str: + shutil.rmtree(dest, ignore_errors=True) + repo_url = f"{repo_prefix}/{repo}" + print(f"Cloning repository {repo_url}") + repo = Repo.clone_from(repo_url, dest) + repo.git.checkout(committish) + return str(dest) + + @classmethod + def __get_library_names_from_config(cls, config: GenerationConfig) -> list[str]: + library_names = [] + for library in config.libraries: + library_names.append(f"java-{library.get_library_name()}") + + return library_names + + @classmethod + def __bind_device_to_volumes(cls, volume_name: str, device_dir: str): + subprocess.check_call(["docker", "volume", "rm", volume_name]) + subprocess.check_call( + [ + "docker", + "volume", + "create", + "--name", + volume_name, + "--opt", + "type=none", + "--opt", + f"device={device_dir}", + "--opt", + "o=bind", + ] + ) + + @classmethod + def __get_config_files(cls, path: str) -> list[tuple[str, str]]: + config_files = [] + for sub_dir in Path(path).resolve().iterdir(): + if sub_dir.is_file(): + continue + repo = sub_dir.name + if repo in ["golden", "java-bigtable"]: + continue + config = f"{sub_dir}/{config_name}" + config_files.append((repo, config)) + return config_files diff --git a/library_generation/test/container_integration_tests.sh b/library_generation/test/container_integration_tests.sh deleted file mode 100644 index a60c733df9..0000000000 --- a/library_generation/test/container_integration_tests.sh +++ /dev/null @@ -1,49 +0,0 @@ -#!/bin/bash -# This is a wrapper of integration_tests.py that sets up the environment to run -# the script in a docker container - -set -xe -if [[ -z "${TEST_IMAGE_ID}" ]]; then - echo "required environemnt variable TEST_IMAGE_ID is not set" - exit 1 -fi - -repo_volumes="" -for repo in google-cloud-java java-bigtable; do - if [[ ! -d "${repo}" ]]; then - git clone "https://github.com/googleapis/${repo}" - fi - pushd "${repo}" - git reset --hard main - popd - - # We use a volume to hold the repositories used in the - # integration tests. This is because the test container creates a child - # container using the host machine's docker socket, meaning that we can only - # reference volumes created from within the host machine (i.e. the machine - # running this script) - # - # To summarize, we create a special volume that can be referenced both in the - # main container and in any child containers created by this one. - volume_name="repo-${repo}" - if [[ $(docker volume inspect "${volume_name}") != '[]' ]]; then - docker volume rm "${volume_name}" - fi - docker volume create \ - --name "${volume_name}" \ - --opt "type=none" \ - --opt "device=$(pwd)/${repo}" \ - --opt "o=bind" - - repo_volumes="${repo_volumes} -v ${volume_name}:/workspace/${repo}" -done - -docker run --rm \ - ${repo_volumes} \ - -v /tmp:/tmp \ - -v /var/run/docker.sock:/var/run/docker.sock \ - -e "RUNNING_IN_DOCKER=true" \ - -e "REPO_BINDING_VOLUMES=${repo_volumes}" \ - -w "/src" \ - "${TEST_IMAGE_ID}" \ - python -m unittest /src/test/integration_tests.py From 3913305ce6f9828cc890ba60c1eda20c933b38c4 Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Sat, 30 Mar 2024 12:56:39 +0000 Subject: [PATCH 02/14] change image tag --- library_generation/test/container_integration_tests.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library_generation/test/container_integration_tests.py b/library_generation/test/container_integration_tests.py index 9b83b304b7..3c69408fc3 100644 --- a/library_generation/test/container_integration_tests.py +++ b/library_generation/test/container_integration_tests.py @@ -18,7 +18,6 @@ import unittest from distutils.dir_util import copy_tree from distutils.file_util import copy_file -import uuid from pathlib import Path from library_generation.model.generation_config import GenerationConfig from library_generation.model.generation_config import from_yaml @@ -30,7 +29,7 @@ docker_file = os.path.join( repo_root_dir, ".cloudbuild", "library_generation", "library_generation.Dockerfile" ) -image_tag = f"test-{str(uuid.uuid4())[:7]}" +image_tag = f"test-image:latest" repo_prefix = "https://github.com/googleapis" output_dir = shell_call("get_output_folder") # this map tells which branch of each repo should we use for our diff tests From 91f9463a976ee60f2e4849cb76e9a93a6e9a8d4a Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Tue, 2 Apr 2024 14:51:14 +0000 Subject: [PATCH 03/14] refactor --- .../test/container_integration_tests.py | 75 +++++++++++-------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/library_generation/test/container_integration_tests.py b/library_generation/test/container_integration_tests.py index 3c69408fc3..485a137de3 100644 --- a/library_generation/test/container_integration_tests.py +++ b/library_generation/test/container_integration_tests.py @@ -26,7 +26,7 @@ script_dir = os.path.dirname(os.path.realpath(__file__)) golden_dir = os.path.join(script_dir, "resources", "integration", "golden") repo_root_dir = os.path.join(script_dir, "..", "..") -docker_file = os.path.join( +build_file = os.path.join( repo_root_dir, ".cloudbuild", "library_generation", "library_generation.Dockerfile" ) image_tag = f"test-image:latest" @@ -43,11 +43,7 @@ class ContainerIntegrationTest(unittest.TestCase): def test_entry_point_running_in_container(self): - # build docker image - subprocess.check_call( - ["docker", "build", "--rm", "-f", docker_file, "-t", image_tag, "."], - cwd=repo_root_dir, - ) + self.__build_image(docker_file=build_file, tag=image_tag, cwd=repo_root_dir) shutil.rmtree(f"{golden_dir}", ignore_errors=True) os.makedirs(f"{golden_dir}", exist_ok=True) @@ -81,34 +77,17 @@ def test_entry_point_running_in_container(self): ) repo_volumes = f"-v repo-{repo}:/workspace/{repo} -v config-{repo}:/workspace/config-{repo}" # run docker container - subprocess.check_call( - [ - "docker", - "run", - "--rm", - "-v", - f"repo-{repo}:/workspace/{repo}", - "-v", - f"config-{repo}:/workspace/config-{repo}", - "-v", - "/tmp:/tmp", - "-v", - "/var/run/docker.sock:/var/run/docker.sock", - "-e", - "RUNNING_IN_DOCKER=true", - "-e", - f"REPO_BINDING_VOLUMES={repo_volumes}", - "-w", - "/src", - image_tag, - "python", - "/src/generate_repo.py", - "generate", - f"--generation-config-yaml=/workspace/config-{repo}/{config_name}", - f"--repository-path=/workspace/{repo}", - ] + self.__run_entry_point_in_docker_container( + tag=image_tag, repo=repo, repo_volumes=repo_volumes ) + @classmethod + def __build_image(cls, docker_file: str, tag: str, cwd: str): + subprocess.check_call( + ["docker", "build", "--rm", "-f", docker_file, "-t", tag, "."], + cwd=cwd, + ) + @classmethod def __pull_repo_to(cls, dest: Path, repo: str, committish: str) -> str: shutil.rmtree(dest, ignore_errors=True) @@ -145,6 +124,38 @@ def __bind_device_to_volumes(cls, volume_name: str, device_dir: str): ] ) + @classmethod + def __run_entry_point_in_docker_container( + cls, tag: str, repo: str, repo_volumes: str + ): + subprocess.check_call( + [ + "docker", + "run", + "--rm", + "-v", + f"repo-{repo}:/workspace/{repo}", + "-v", + f"config-{repo}:/workspace/config-{repo}", + "-v", + "/tmp:/tmp", + "-v", + "/var/run/docker.sock:/var/run/docker.sock", + "-e", + "RUNNING_IN_DOCKER=true", + "-e", + f"REPO_BINDING_VOLUMES={repo_volumes}", + "-w", + "/src", + tag, + "python", + "/src/generate_repo.py", + "generate", + f"--generation-config-yaml=/workspace/config-{repo}/{config_name}", + f"--repository-path=/workspace/{repo}", + ] + ) + @classmethod def __get_config_files(cls, path: str) -> list[tuple[str, str]]: config_files = [] From e47a32891d408386c85fae6b74973fb7383bfd26 Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Tue, 2 Apr 2024 15:09:49 +0000 Subject: [PATCH 04/14] add file comparison --- .../test/container_integration_tests.py | 155 ++++++++++++++++-- 1 file changed, 139 insertions(+), 16 deletions(-) diff --git a/library_generation/test/container_integration_tests.py b/library_generation/test/container_integration_tests.py index 485a137de3..ac215215ef 100644 --- a/library_generation/test/container_integration_tests.py +++ b/library_generation/test/container_integration_tests.py @@ -11,6 +11,9 @@ # 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 json +from filecmp import dircmp + from git import Repo import os import shutil @@ -21,6 +24,7 @@ from pathlib import Path from library_generation.model.generation_config import GenerationConfig from library_generation.model.generation_config import from_yaml +from library_generation.test.compare_poms import compare_xml from library_generation.utilities import sh_util as shell_call script_dir = os.path.dirname(os.path.realpath(__file__)) @@ -50,36 +54,104 @@ def test_entry_point_running_in_container(self): config_files = self.__get_config_files(config_dir) for repo, config_file in config_files: config = from_yaml(config_file) + # 1. pull repository repo_dest = self.__pull_repo_to( Path(f"{output_dir}/{repo}"), repo, committish_map[repo] ) + # 2. prepare golden files library_names = self.__get_library_names_from_config(config) - # prepare golden files - for library_name in library_names: - if config.is_monorepo: - copy_tree( - f"{repo_dest}/{library_name}", f"{golden_dir}/{library_name}" - ) - copy_tree( - f"{repo_dest}/gapic-libraries-bom", - f"{golden_dir}/gapic-libraries-bom", - ) - copy_file(f"{repo_dest}/pom.xml", golden_dir) - else: - copy_tree(f"{repo_dest}", f"{golden_dir}/{library_name}") - # bind repository to docker volumes + self.__prepare_golden_files( + config=config, library_names=library_names, repo_dest=repo_dest + ) + # 3. bind repository and configuration to docker volumes self.__bind_device_to_volumes( volume_name=f"repo-{repo}", device_dir=f"{output_dir}/{repo}" ) - # bind configuration to docker volumes self.__bind_device_to_volumes( volume_name=f"config-{repo}", device_dir=f"{golden_dir}/../{repo}" ) repo_volumes = f"-v repo-{repo}:/workspace/{repo} -v config-{repo}:/workspace/config-{repo}" - # run docker container + # 4. run entry_point.py in docker container self.__run_entry_point_in_docker_container( tag=image_tag, repo=repo, repo_volumes=repo_volumes ) + # 5. compare generation result with golden files + print( + "Generation finished successfully. " + "Will now compare differences between generated and existing " + "libraries" + ) + for library_name in library_names: + actual_library = ( + f"{repo_dest}/{library_name}" if config.is_monorepo else repo_dest + ) + print("*" * 50) + print(f"Checking for differences in '{library_name}'.") + print(f" The expected library is in {golden_dir}/{library_name}.") + print(f" The actual library is in {actual_library}. ") + target_repo_dest = ( + f"{repo_dest}/{library_name}" if config.is_monorepo else repo_dest + ) + compare_result = dircmp( + f"{golden_dir}/{library_name}", + target_repo_dest, + ignore=[".repo-metadata.json"], + ) + diff_files = [] + golden_only = [] + generated_only = [] + # compare source code + self.__recursive_diff_files( + compare_result, diff_files, golden_only, generated_only + ) + + # print all found differences for inspection + print_file = lambda f: print(f" - {f}") + if len(diff_files) > 0: + print(" Some files (found in both folders) are differing:") + [print_file(f) for f in diff_files] + if len(golden_only) > 0: + print(" There were files found only in the golden dir:") + [print_file(f) for f in golden_only] + if len(generated_only) > 0: + print(" Some files were found to have differences:") + [print_file(f) for f in generated_only] + + self.assertTrue(len(golden_only) == 0) + self.assertTrue(len(generated_only) == 0) + self.assertTrue(len(diff_files) == 0) + + print(" No differences found in {library_name}") + # compare .repo-metadata.json + self.assertTrue( + self.__compare_json_files( + f"{golden_dir}/{library_name}/.repo-metadata.json", + f"{target_repo_dest}/.repo-metadata.json", + ), + msg=f" The generated {library_name}/.repo-metadata.json is different from golden.", + ) + print(" .repo-metadata.json comparison succeed.") + + if not config.is_monorepo: + continue + + # compare gapic-libraries-bom/pom.xml and pom.xml + self.assertFalse( + compare_xml( + f"{golden_dir}/gapic-libraries-bom/pom.xml", + f"{repo_dest}/gapic-libraries-bom/pom.xml", + False, + ) + ) + print(" gapic-libraries-bom/pom.xml comparison succeed.") + self.assertFalse( + compare_xml( + f"{golden_dir}/pom.xml", + f"{repo_dest}/pom.xml", + False, + ) + ) + print(" pom.xml comparison succeed.") @classmethod def __build_image(cls, docker_file: str, tag: str, cwd: str): @@ -105,6 +177,21 @@ def __get_library_names_from_config(cls, config: GenerationConfig) -> list[str]: return library_names + @classmethod + def __prepare_golden_files( + cls, config: GenerationConfig, library_names: list[str], repo_dest: str + ): + for library_name in library_names: + if config.is_monorepo: + copy_tree(f"{repo_dest}/{library_name}", f"{golden_dir}/{library_name}") + copy_tree( + f"{repo_dest}/gapic-libraries-bom", + f"{golden_dir}/gapic-libraries-bom", + ) + copy_file(f"{repo_dest}/pom.xml", golden_dir) + else: + copy_tree(f"{repo_dest}", f"{golden_dir}/{library_name}") + @classmethod def __bind_device_to_volumes(cls, volume_name: str, device_dir: str): subprocess.check_call(["docker", "volume", "rm", volume_name]) @@ -168,3 +255,39 @@ def __get_config_files(cls, path: str) -> list[tuple[str, str]]: config = f"{sub_dir}/{config_name}" config_files.append((repo, config)) return config_files + + @classmethod + def __compare_json_files(cls, expected: str, actual: str) -> bool: + return cls.__load_json_to_sorted_list( + expected + ) == cls.__load_json_to_sorted_list(actual) + + @classmethod + def __load_json_to_sorted_list(cls, path: str) -> list[tuple]: + with open(path) as f: + data = json.load(f) + res = [(key, value) for key, value in data.items()] + + return sorted(res, key=lambda x: x[0]) + + @classmethod + def __recursive_diff_files( + cls, + dcmp: dircmp, + diff_files: list[str], + left_only: list[str], + right_only: list[str], + dirname: str = "", + ): + """ + Recursively compares two subdirectories. The found differences are + passed to three expected list references. + """ + append_dirname = lambda d: dirname + d + diff_files.extend(map(append_dirname, dcmp.diff_files)) + left_only.extend(map(append_dirname, dcmp.left_only)) + right_only.extend(map(append_dirname, dcmp.right_only)) + for sub_dirname, sub_dcmp in dcmp.subdirs.items(): + cls.__recursive_diff_files( + sub_dcmp, diff_files, left_only, right_only, dirname + sub_dirname + "/" + ) From 83c13e4cda94a3338765b7a8706709799e528c4a Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Tue, 2 Apr 2024 15:48:03 +0000 Subject: [PATCH 05/14] add pr description comparison --- .../test/container_integration_tests.py | 293 ------------------ library_generation/test/integration_tests.py | 268 ++++++++++------ 2 files changed, 171 insertions(+), 390 deletions(-) delete mode 100644 library_generation/test/container_integration_tests.py mode change 100755 => 100644 library_generation/test/integration_tests.py diff --git a/library_generation/test/container_integration_tests.py b/library_generation/test/container_integration_tests.py deleted file mode 100644 index ac215215ef..0000000000 --- a/library_generation/test/container_integration_tests.py +++ /dev/null @@ -1,293 +0,0 @@ -# Copyright 2024 Google LLC -# -# 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 -# -# https://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 json -from filecmp import dircmp - -from git import Repo -import os -import shutil -import subprocess -import unittest -from distutils.dir_util import copy_tree -from distutils.file_util import copy_file -from pathlib import Path -from library_generation.model.generation_config import GenerationConfig -from library_generation.model.generation_config import from_yaml -from library_generation.test.compare_poms import compare_xml -from library_generation.utilities import sh_util as shell_call - -script_dir = os.path.dirname(os.path.realpath(__file__)) -golden_dir = os.path.join(script_dir, "resources", "integration", "golden") -repo_root_dir = os.path.join(script_dir, "..", "..") -build_file = os.path.join( - repo_root_dir, ".cloudbuild", "library_generation", "library_generation.Dockerfile" -) -image_tag = f"test-image:latest" -repo_prefix = "https://github.com/googleapis" -output_dir = shell_call("get_output_folder") -# this map tells which branch of each repo should we use for our diff tests -committish_map = { - "google-cloud-java": "chore/test-hermetic-build", - "java-bigtable": "chore/test-hermetic-build", -} -config_dir = f"{script_dir}/resources/integration" -config_name = "generation_config.yaml" - - -class ContainerIntegrationTest(unittest.TestCase): - def test_entry_point_running_in_container(self): - self.__build_image(docker_file=build_file, tag=image_tag, cwd=repo_root_dir) - - shutil.rmtree(f"{golden_dir}", ignore_errors=True) - os.makedirs(f"{golden_dir}", exist_ok=True) - config_files = self.__get_config_files(config_dir) - for repo, config_file in config_files: - config = from_yaml(config_file) - # 1. pull repository - repo_dest = self.__pull_repo_to( - Path(f"{output_dir}/{repo}"), repo, committish_map[repo] - ) - # 2. prepare golden files - library_names = self.__get_library_names_from_config(config) - self.__prepare_golden_files( - config=config, library_names=library_names, repo_dest=repo_dest - ) - # 3. bind repository and configuration to docker volumes - self.__bind_device_to_volumes( - volume_name=f"repo-{repo}", device_dir=f"{output_dir}/{repo}" - ) - self.__bind_device_to_volumes( - volume_name=f"config-{repo}", device_dir=f"{golden_dir}/../{repo}" - ) - repo_volumes = f"-v repo-{repo}:/workspace/{repo} -v config-{repo}:/workspace/config-{repo}" - # 4. run entry_point.py in docker container - self.__run_entry_point_in_docker_container( - tag=image_tag, repo=repo, repo_volumes=repo_volumes - ) - # 5. compare generation result with golden files - print( - "Generation finished successfully. " - "Will now compare differences between generated and existing " - "libraries" - ) - for library_name in library_names: - actual_library = ( - f"{repo_dest}/{library_name}" if config.is_monorepo else repo_dest - ) - print("*" * 50) - print(f"Checking for differences in '{library_name}'.") - print(f" The expected library is in {golden_dir}/{library_name}.") - print(f" The actual library is in {actual_library}. ") - target_repo_dest = ( - f"{repo_dest}/{library_name}" if config.is_monorepo else repo_dest - ) - compare_result = dircmp( - f"{golden_dir}/{library_name}", - target_repo_dest, - ignore=[".repo-metadata.json"], - ) - diff_files = [] - golden_only = [] - generated_only = [] - # compare source code - self.__recursive_diff_files( - compare_result, diff_files, golden_only, generated_only - ) - - # print all found differences for inspection - print_file = lambda f: print(f" - {f}") - if len(diff_files) > 0: - print(" Some files (found in both folders) are differing:") - [print_file(f) for f in diff_files] - if len(golden_only) > 0: - print(" There were files found only in the golden dir:") - [print_file(f) for f in golden_only] - if len(generated_only) > 0: - print(" Some files were found to have differences:") - [print_file(f) for f in generated_only] - - self.assertTrue(len(golden_only) == 0) - self.assertTrue(len(generated_only) == 0) - self.assertTrue(len(diff_files) == 0) - - print(" No differences found in {library_name}") - # compare .repo-metadata.json - self.assertTrue( - self.__compare_json_files( - f"{golden_dir}/{library_name}/.repo-metadata.json", - f"{target_repo_dest}/.repo-metadata.json", - ), - msg=f" The generated {library_name}/.repo-metadata.json is different from golden.", - ) - print(" .repo-metadata.json comparison succeed.") - - if not config.is_monorepo: - continue - - # compare gapic-libraries-bom/pom.xml and pom.xml - self.assertFalse( - compare_xml( - f"{golden_dir}/gapic-libraries-bom/pom.xml", - f"{repo_dest}/gapic-libraries-bom/pom.xml", - False, - ) - ) - print(" gapic-libraries-bom/pom.xml comparison succeed.") - self.assertFalse( - compare_xml( - f"{golden_dir}/pom.xml", - f"{repo_dest}/pom.xml", - False, - ) - ) - print(" pom.xml comparison succeed.") - - @classmethod - def __build_image(cls, docker_file: str, tag: str, cwd: str): - subprocess.check_call( - ["docker", "build", "--rm", "-f", docker_file, "-t", tag, "."], - cwd=cwd, - ) - - @classmethod - def __pull_repo_to(cls, dest: Path, repo: str, committish: str) -> str: - shutil.rmtree(dest, ignore_errors=True) - repo_url = f"{repo_prefix}/{repo}" - print(f"Cloning repository {repo_url}") - repo = Repo.clone_from(repo_url, dest) - repo.git.checkout(committish) - return str(dest) - - @classmethod - def __get_library_names_from_config(cls, config: GenerationConfig) -> list[str]: - library_names = [] - for library in config.libraries: - library_names.append(f"java-{library.get_library_name()}") - - return library_names - - @classmethod - def __prepare_golden_files( - cls, config: GenerationConfig, library_names: list[str], repo_dest: str - ): - for library_name in library_names: - if config.is_monorepo: - copy_tree(f"{repo_dest}/{library_name}", f"{golden_dir}/{library_name}") - copy_tree( - f"{repo_dest}/gapic-libraries-bom", - f"{golden_dir}/gapic-libraries-bom", - ) - copy_file(f"{repo_dest}/pom.xml", golden_dir) - else: - copy_tree(f"{repo_dest}", f"{golden_dir}/{library_name}") - - @classmethod - def __bind_device_to_volumes(cls, volume_name: str, device_dir: str): - subprocess.check_call(["docker", "volume", "rm", volume_name]) - subprocess.check_call( - [ - "docker", - "volume", - "create", - "--name", - volume_name, - "--opt", - "type=none", - "--opt", - f"device={device_dir}", - "--opt", - "o=bind", - ] - ) - - @classmethod - def __run_entry_point_in_docker_container( - cls, tag: str, repo: str, repo_volumes: str - ): - subprocess.check_call( - [ - "docker", - "run", - "--rm", - "-v", - f"repo-{repo}:/workspace/{repo}", - "-v", - f"config-{repo}:/workspace/config-{repo}", - "-v", - "/tmp:/tmp", - "-v", - "/var/run/docker.sock:/var/run/docker.sock", - "-e", - "RUNNING_IN_DOCKER=true", - "-e", - f"REPO_BINDING_VOLUMES={repo_volumes}", - "-w", - "/src", - tag, - "python", - "/src/generate_repo.py", - "generate", - f"--generation-config-yaml=/workspace/config-{repo}/{config_name}", - f"--repository-path=/workspace/{repo}", - ] - ) - - @classmethod - def __get_config_files(cls, path: str) -> list[tuple[str, str]]: - config_files = [] - for sub_dir in Path(path).resolve().iterdir(): - if sub_dir.is_file(): - continue - repo = sub_dir.name - if repo in ["golden", "java-bigtable"]: - continue - config = f"{sub_dir}/{config_name}" - config_files.append((repo, config)) - return config_files - - @classmethod - def __compare_json_files(cls, expected: str, actual: str) -> bool: - return cls.__load_json_to_sorted_list( - expected - ) == cls.__load_json_to_sorted_list(actual) - - @classmethod - def __load_json_to_sorted_list(cls, path: str) -> list[tuple]: - with open(path) as f: - data = json.load(f) - res = [(key, value) for key, value in data.items()] - - return sorted(res, key=lambda x: x[0]) - - @classmethod - def __recursive_diff_files( - cls, - dcmp: dircmp, - diff_files: list[str], - left_only: list[str], - right_only: list[str], - dirname: str = "", - ): - """ - Recursively compares two subdirectories. The found differences are - passed to three expected list references. - """ - append_dirname = lambda d: dirname + d - diff_files.extend(map(append_dirname, dcmp.diff_files)) - left_only.extend(map(append_dirname, dcmp.left_only)) - right_only.extend(map(append_dirname, dcmp.right_only)) - for sub_dirname, sub_dcmp in dcmp.subdirs.items(): - cls.__recursive_diff_files( - sub_dcmp, diff_files, left_only, right_only, dirname + sub_dirname + "/" - ) diff --git a/library_generation/test/integration_tests.py b/library_generation/test/integration_tests.py old mode 100755 new mode 100644 index 35cbf565ac..340459c375 --- a/library_generation/test/integration_tests.py +++ b/library_generation/test/integration_tests.py @@ -12,34 +12,28 @@ # See the License for the specific language governing permissions and # limitations under the License. import json +from filecmp import cmp +from filecmp import dircmp +from git import Repo import os import shutil +import subprocess import unittest from distutils.dir_util import copy_tree from distutils.file_util import copy_file -from filecmp import cmp -from filecmp import dircmp - -from git import Repo from pathlib import Path -from typing import List - -from library_generation.generate_pr_description import generate_pr_descriptions -from library_generation.generate_repo import generate_from_yaml -from library_generation.model.generation_config import from_yaml, GenerationConfig +from library_generation.model.generation_config import GenerationConfig +from library_generation.model.generation_config import from_yaml from library_generation.test.compare_poms import compare_xml -from library_generation.utilities import ( - sh_util as shell_call, - run_process_and_print_output, -) +from library_generation.utilities import sh_util as shell_call -config_name = "generation_config.yaml" script_dir = os.path.dirname(os.path.realpath(__file__)) -# for simplicity, the configuration files should be in a relative directory -# within config_dir named {repo}/generation_config.yaml, where repo is -# the name of the repository the target libraries live. -config_dir = f"{script_dir}/resources/integration" -golden_dir = f"{config_dir}/golden" +golden_dir = os.path.join(script_dir, "resources", "integration", "golden") +repo_root_dir = os.path.join(script_dir, "..", "..") +build_file = os.path.join( + repo_root_dir, ".cloudbuild", "library_generation", "library_generation.Dockerfile" +) +image_tag = f"test-image:latest" repo_prefix = "https://github.com/googleapis" output_dir = shell_call("get_output_folder") # this map tells which branch of each repo should we use for our diff tests @@ -47,68 +41,55 @@ "google-cloud-java": "chore/test-hermetic-build", "java-bigtable": "chore/test-hermetic-build", } +config_dir = f"{script_dir}/resources/integration" +config_name = "generation_config.yaml" +monorepo_baseline_commit = "a17d4caf184b050d50cacf2b0d579ce72c31ce74" +split_repo_baseline_commit = "679060c64136e85b52838f53cfe612ce51e60d1d" -class IntegrationTest(unittest.TestCase): - def test_get_commit_message_success(self): - repo_url = "https://github.com/googleapis/googleapis.git" - config_files = self.__get_config_files(config_dir) - monorepo_baseline_commit = "a17d4caf184b050d50cacf2b0d579ce72c31ce74" - split_repo_baseline_commit = "679060c64136e85b52838f53cfe612ce51e60d1d" - for repo, config_file in config_files: - baseline_commit = ( - monorepo_baseline_commit - if repo == "google-cloud-java" - else split_repo_baseline_commit - ) - description = generate_pr_descriptions( - generation_config_yaml=config_file, - repo_url=repo_url, - baseline_commit=baseline_commit, - ) - description_file = f"{config_dir}/{repo}/pr-description.txt" - if os.path.isfile(f"{description_file}"): - os.remove(f"{description_file}") - with open(f"{description_file}", "w+") as f: - f.write(description) - self.assertTrue( - cmp( - f"{config_dir}/{repo}/pr-description-golden.txt", - f"{description_file}", - ), - "The generated PR description does not match the expected golden file", - ) - os.remove(f"{description_file}") +class ContainerIntegrationTest(unittest.TestCase): + def test_entry_point_running_in_container(self): + self.__build_image(docker_file=build_file, tag=image_tag, cwd=repo_root_dir) - def test_generate_repo(self): shutil.rmtree(f"{golden_dir}", ignore_errors=True) os.makedirs(f"{golden_dir}", exist_ok=True) config_files = self.__get_config_files(config_dir) for repo, config_file in config_files: config = from_yaml(config_file) + # 1. pull repository repo_dest = self.__pull_repo_to( Path(f"{output_dir}/{repo}"), repo, committish_map[repo] ) + # 2. prepare golden files library_names = self.__get_library_names_from_config(config) - # prepare golden files - for library_name in library_names: - if config.is_monorepo: - copy_tree( - f"{repo_dest}/{library_name}", f"{golden_dir}/{library_name}" - ) - copy_tree( - f"{repo_dest}/gapic-libraries-bom", - f"{golden_dir}/gapic-libraries-bom", - ) - copy_file(f"{repo_dest}/pom.xml", golden_dir) - else: - copy_tree(f"{repo_dest}", f"{golden_dir}/{library_name}") - generate_from_yaml( - generation_config_yaml=config_file, repository_path=repo_dest + self.__prepare_golden_files( + config=config, library_names=library_names, repo_dest=repo_dest ) - # compare result + # 3. bind repository and configuration to docker volumes + self.__bind_device_to_volumes( + volume_name=f"repo-{repo}", device_dir=f"{output_dir}/{repo}" + ) + self.__bind_device_to_volumes( + volume_name=f"config-{repo}", device_dir=f"{golden_dir}/../{repo}" + ) + repo_volumes = f"-v repo-{repo}:/workspace/{repo} -v config-{repo}:/workspace/config-{repo}" + # 4. run entry_point.py in docker container + baseline_commit = ( + monorepo_baseline_commit + if repo == "google-cloud-java" + else split_repo_baseline_commit + ) + self.__run_entry_point_in_docker_container( + tag=image_tag, + repo=repo, + repo_volumes=repo_volumes, + baseline_commit=baseline_commit, + ) + # 5. compare generation result with golden files print( - "Generation finished successfully. Will now compare differences between generated and existing libraries" + "Generation finished successfully. " + "Will now compare differences between generated and existing " + "libraries" ) for library_name in library_names: actual_library = ( @@ -181,36 +162,36 @@ def test_generate_repo(self): ) ) print(" pom.xml comparison succeed.") + # compare PR description + description_file = f"{config_dir}/{repo}/pr_description.txt" + self.assertTrue( + cmp( + f"{config_dir}/{repo}/pr-description-golden.txt", + f"{description_file}", + ), + "The generated PR description does not match the expected golden file", + ) + print(" PR description comparison succeed.") + os.remove(description_file) @classmethod - def __pull_repo_to(cls, default_dest: Path, repo: str, committish: str) -> str: - if "RUNNING_IN_DOCKER" in os.environ: - # the docker image expects the repo to be in /workspace - dest_in_docker = f"/workspace/{repo}" - run_process_and_print_output( - [ - "git", - "config", - "--global", - "--add", - "safe.directory", - dest_in_docker, - ], - f"Add /workspace/{repo} to safe directories", - ) - dest = Path(dest_in_docker) - repo = Repo(dest) - else: - dest = default_dest - shutil.rmtree(dest, ignore_errors=True) - repo_url = f"{repo_prefix}/{repo}" - print(f"Cloning repository {repo_url}") - repo = Repo.clone_from(repo_url, dest) + def __build_image(cls, docker_file: str, tag: str, cwd: str): + subprocess.check_call( + ["docker", "build", "--rm", "-f", docker_file, "-t", tag, "."], + cwd=cwd, + ) + + @classmethod + def __pull_repo_to(cls, dest: Path, repo: str, committish: str) -> str: + shutil.rmtree(dest, ignore_errors=True) + repo_url = f"{repo_prefix}/{repo}" + print(f"Cloning repository {repo_url}") + repo = Repo.clone_from(repo_url, dest) repo.git.checkout(committish) return str(dest) @classmethod - def __get_library_names_from_config(cls, config: GenerationConfig) -> List[str]: + def __get_library_names_from_config(cls, config: GenerationConfig) -> list[str]: library_names = [] for library in config.libraries: library_names.append(f"java-{library.get_library_name()}") @@ -218,7 +199,101 @@ def __get_library_names_from_config(cls, config: GenerationConfig) -> List[str]: return library_names @classmethod - def __get_config_files(cls, path: str) -> List[tuple[str, str]]: + def __prepare_golden_files( + cls, config: GenerationConfig, library_names: list[str], repo_dest: str + ): + for library_name in library_names: + if config.is_monorepo: + copy_tree(f"{repo_dest}/{library_name}", f"{golden_dir}/{library_name}") + copy_tree( + f"{repo_dest}/gapic-libraries-bom", + f"{golden_dir}/gapic-libraries-bom", + ) + copy_file(f"{repo_dest}/pom.xml", golden_dir) + else: + copy_tree(f"{repo_dest}", f"{golden_dir}/{library_name}") + + @classmethod + def __bind_device_to_volumes(cls, volume_name: str, device_dir: str): + subprocess.check_call(["docker", "volume", "rm", volume_name]) + subprocess.check_call( + [ + "docker", + "volume", + "create", + "--name", + volume_name, + "--opt", + "type=none", + "--opt", + f"device={device_dir}", + "--opt", + "o=bind", + ] + ) + + @classmethod + def __run_entry_point_in_docker_container( + cls, tag: str, repo: str, repo_volumes: str, baseline_commit: str + ): + subprocess.check_call( + [ + "docker", + "run", + "--rm", + "-v", + f"repo-{repo}:/workspace/{repo}", + "-v", + f"config-{repo}:/workspace/config-{repo}", + "-v", + "/tmp:/tmp", + "-v", + "/var/run/docker.sock:/var/run/docker.sock", + "-e", + "RUNNING_IN_DOCKER=true", + "-e", + f"REPO_BINDING_VOLUMES={repo_volumes}", + "-w", + "/src", + tag, + "python", + "/src/generate_repo.py", + "generate", + f"--generation-config-yaml=/workspace/config-{repo}/{config_name}", + f"--repository-path=/workspace/{repo}", + ] + ) + + subprocess.check_call( + [ + "docker", + "run", + "--rm", + "-v", + f"repo-{repo}:/workspace/{repo}", + "-v", + f"config-{repo}:/workspace/config-{repo}", + "-v", + "/tmp:/tmp", + "-v", + "/var/run/docker.sock:/var/run/docker.sock", + "-e", + "RUNNING_IN_DOCKER=true", + "-e", + f"REPO_BINDING_VOLUMES={repo_volumes}", + "-w", + "/src", + tag, + "python", + "/src/generate_pr_description.py", + "generate", + f"--generation-config-yaml=/workspace/config-{repo}/{config_name}", + f"--baseline-commit={baseline_commit}", + ] + ) + + @classmethod + def __get_config_files(cls, path: str) -> list[tuple[str, str]]: config_files = [] for sub_dir in Path(path).resolve().iterdir(): if sub_dir.is_file(): @@ -228,7 +303,6 @@ def __get_config_files(cls, path: str) -> List[tuple[str, str]]: continue config = f"{sub_dir}/{config_name}" config_files.append((repo, config)) - return config_files @classmethod @@ -238,7 +312,7 @@ def __compare_json_files(cls, expected: str, actual: str) -> bool: ) == cls.__load_json_to_sorted_list(actual) @classmethod - def __load_json_to_sorted_list(cls, path: str) -> List[tuple]: + def __load_json_to_sorted_list(cls, path: str) -> list[tuple]: with open(path) as f: data = json.load(f) res = [(key, value) for key, value in data.items()] @@ -249,9 +323,9 @@ def __load_json_to_sorted_list(cls, path: str) -> List[tuple]: def __recursive_diff_files( cls, dcmp: dircmp, - diff_files: List[str], - left_only: List[str], - right_only: List[str], + diff_files: list[str], + left_only: list[str], + right_only: list[str], dirname: str = "", ): """ From ca323ee91082746703789f04f5de53627234c8f5 Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Tue, 2 Apr 2024 16:00:31 +0000 Subject: [PATCH 06/14] refactor --- library_generation/test/integration_tests.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library_generation/test/integration_tests.py b/library_generation/test/integration_tests.py index 340459c375..04d4cdf425 100644 --- a/library_generation/test/integration_tests.py +++ b/library_generation/test/integration_tests.py @@ -47,7 +47,7 @@ split_repo_baseline_commit = "679060c64136e85b52838f53cfe612ce51e60d1d" -class ContainerIntegrationTest(unittest.TestCase): +class IntegrationTest(unittest.TestCase): def test_entry_point_running_in_container(self): self.__build_image(docker_file=build_file, tag=image_tag, cwd=repo_root_dir) @@ -172,7 +172,6 @@ def test_entry_point_running_in_container(self): "The generated PR description does not match the expected golden file", ) print(" PR description comparison succeed.") - os.remove(description_file) @classmethod def __build_image(cls, docker_file: str, tag: str, cwd: str): From a9512e25fa79097336db78c16e732ff63add227a Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Tue, 2 Apr 2024 16:09:29 +0000 Subject: [PATCH 07/14] change workflow --- .github/workflows/verify_library_generation.yaml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/verify_library_generation.yaml b/.github/workflows/verify_library_generation.yaml index b5707b443d..ca308461c0 100644 --- a/.github/workflows/verify_library_generation.yaml +++ b/.github/workflows/verify_library_generation.yaml @@ -48,8 +48,6 @@ jobs: shell: bash run: | set -x - git config --global user.email "github-workflow@github.com" - git config --global user.name "Github Workflow" python -m unittest library_generation/test/integration_tests.py unit_tests: strategy: From f878409cea9022ea325d06df3d62e8f1f103898d Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Tue, 2 Apr 2024 16:14:05 +0000 Subject: [PATCH 08/14] remove setup steps in IT --- .../workflows/verify_library_generation.yaml | 23 ------------------- 1 file changed, 23 deletions(-) diff --git a/.github/workflows/verify_library_generation.yaml b/.github/workflows/verify_library_generation.yaml index ca308461c0..f8e3bb1268 100644 --- a/.github/workflows/verify_library_generation.yaml +++ b/.github/workflows/verify_library_generation.yaml @@ -21,29 +21,6 @@ jobs: java-version: ${{ matrix.java }} distribution: temurin cache: maven - - uses: actions/setup-python@v5 - with: - python-version: 3.11 - - name: install pyenv - shell: bash - run: | - set -ex - curl https://pyenv.run | bash - # setup environment - export PYENV_ROOT="$HOME/.pyenv" - export PATH="$PYENV_ROOT/bin:$PATH" - echo "PYENV_ROOT=${PYENV_ROOT}" >> $GITHUB_ENV - echo "PATH=${PATH}" >> $GITHUB_ENV - - set +ex - - name: install python dependencies - shell: bash - run: | - set -ex - pushd library_generation - pip install -r requirements.in - pip install . - popd - name: Run integration tests shell: bash run: | From 46677483faf4acb6e995b0a78b1124ecda92bf31 Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Tue, 2 Apr 2024 16:16:26 +0000 Subject: [PATCH 09/14] restore workflow --- .../workflows/verify_library_generation.yaml | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/.github/workflows/verify_library_generation.yaml b/.github/workflows/verify_library_generation.yaml index f8e3bb1268..ca308461c0 100644 --- a/.github/workflows/verify_library_generation.yaml +++ b/.github/workflows/verify_library_generation.yaml @@ -21,6 +21,29 @@ jobs: java-version: ${{ matrix.java }} distribution: temurin cache: maven + - uses: actions/setup-python@v5 + with: + python-version: 3.11 + - name: install pyenv + shell: bash + run: | + set -ex + curl https://pyenv.run | bash + # setup environment + export PYENV_ROOT="$HOME/.pyenv" + export PATH="$PYENV_ROOT/bin:$PATH" + echo "PYENV_ROOT=${PYENV_ROOT}" >> $GITHUB_ENV + echo "PATH=${PATH}" >> $GITHUB_ENV + + set +ex + - name: install python dependencies + shell: bash + run: | + set -ex + pushd library_generation + pip install -r requirements.in + pip install . + popd - name: Run integration tests shell: bash run: | From 97b5c07b90f35cd10e18c2500a1161b7fb1e8bc8 Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Tue, 2 Apr 2024 17:03:06 +0000 Subject: [PATCH 10/14] remove docker volume if it exists --- library_generation/test/integration_tests.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/library_generation/test/integration_tests.py b/library_generation/test/integration_tests.py index 04d4cdf425..08dd2470c9 100644 --- a/library_generation/test/integration_tests.py +++ b/library_generation/test/integration_tests.py @@ -214,7 +214,8 @@ def __prepare_golden_files( @classmethod def __bind_device_to_volumes(cls, volume_name: str, device_dir: str): - subprocess.check_call(["docker", "volume", "rm", volume_name]) + if subprocess.check_output(["docker", "volume", "inspect", volume_name]) != "[]": + subprocess.check_call(["docker", "volume", "rm", volume_name]) subprocess.check_call( [ "docker", From 2336cbde60d7b4e71b771fb070472203a8859c7b Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Tue, 2 Apr 2024 17:03:55 +0000 Subject: [PATCH 11/14] format code --- library_generation/test/integration_tests.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library_generation/test/integration_tests.py b/library_generation/test/integration_tests.py index 08dd2470c9..8d91b447ea 100644 --- a/library_generation/test/integration_tests.py +++ b/library_generation/test/integration_tests.py @@ -214,7 +214,10 @@ def __prepare_golden_files( @classmethod def __bind_device_to_volumes(cls, volume_name: str, device_dir: str): - if subprocess.check_output(["docker", "volume", "inspect", volume_name]) != "[]": + if ( + subprocess.check_output(["docker", "volume", "inspect", volume_name]) + != "[]" + ): subprocess.check_call(["docker", "volume", "rm", volume_name]) subprocess.check_call( [ From cf207eceaf2a7df99181a9a4cb96145c555b0ed5 Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Tue, 2 Apr 2024 19:43:17 +0000 Subject: [PATCH 12/14] add comments --- library_generation/test/integration_tests.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/library_generation/test/integration_tests.py b/library_generation/test/integration_tests.py index 383206e538..ef1b8f1f99 100644 --- a/library_generation/test/integration_tests.py +++ b/library_generation/test/integration_tests.py @@ -214,6 +214,17 @@ def __prepare_golden_files( @classmethod def __bind_device_to_volumes(cls, volume_name: str, device_dir: str): + # We use a volume to hold the repositories used in the integration + # tests. This is because the test container creates a child container + # using the host machine's docker socket, meaning that we can only + # reference volumes created from within the host machine (i.e. the + # machine running this script). + # + # To summarize, we create a special volume that can be referenced both + # in the main container and in any child containers created by this one. + + # use subprocess.run because we don't care about the return value (we + # want to remove the volume in any case). subprocess.run(["docker", "volume", "rm", volume_name]) subprocess.check_call( [ From 712233889c71c1146cbf44c76cc4a7f161393d22 Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Tue, 2 Apr 2024 21:31:47 +0000 Subject: [PATCH 13/14] refactor --- library_generation/test/integration_tests.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/library_generation/test/integration_tests.py b/library_generation/test/integration_tests.py index ef1b8f1f99..797e66701e 100644 --- a/library_generation/test/integration_tests.py +++ b/library_generation/test/integration_tests.py @@ -33,7 +33,7 @@ build_file = os.path.join( repo_root_dir, ".cloudbuild", "library_generation", "library_generation.Dockerfile" ) -image_tag = f"test-image:latest" +image_tag = "test-image:latest" repo_prefix = "https://github.com/googleapis" output_dir = shell_call("get_output_folder") # this map tells which branch of each repo should we use for our diff tests @@ -80,7 +80,6 @@ def test_entry_point_running_in_container(self): else split_repo_baseline_commit ) self.__run_entry_point_in_docker_container( - tag=image_tag, repo=repo, repo_volumes=repo_volumes, baseline_commit=baseline_commit, @@ -244,7 +243,7 @@ def __bind_device_to_volumes(cls, volume_name: str, device_dir: str): @classmethod def __run_entry_point_in_docker_container( - cls, tag: str, repo: str, repo_volumes: str, baseline_commit: str + cls, repo: str, repo_volumes: str, baseline_commit: str ): subprocess.check_call( [ @@ -265,7 +264,7 @@ def __run_entry_point_in_docker_container( f"REPO_BINDING_VOLUMES={repo_volumes}", "-w", "/src", - tag, + image_tag, "python", "/src/generate_repo.py", "generate", @@ -293,7 +292,7 @@ def __run_entry_point_in_docker_container( f"REPO_BINDING_VOLUMES={repo_volumes}", "-w", "/src", - tag, + image_tag, "python", "/src/generate_pr_description.py", "generate", From 9a946ba093b9b3bcef883bc3c131bf78e65ef5e1 Mon Sep 17 00:00:00 2001 From: JoeWang1127 Date: Tue, 2 Apr 2024 21:34:07 +0000 Subject: [PATCH 14/14] refactor --- library_generation/test/integration_tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library_generation/test/integration_tests.py b/library_generation/test/integration_tests.py index 797e66701e..2acfa7d3b9 100644 --- a/library_generation/test/integration_tests.py +++ b/library_generation/test/integration_tests.py @@ -49,7 +49,7 @@ class IntegrationTest(unittest.TestCase): def test_entry_point_running_in_container(self): - self.__build_image(docker_file=build_file, tag=image_tag, cwd=repo_root_dir) + self.__build_image(docker_file=build_file, cwd=repo_root_dir) shutil.rmtree(f"{golden_dir}", ignore_errors=True) os.makedirs(f"{golden_dir}", exist_ok=True) @@ -173,9 +173,9 @@ def test_entry_point_running_in_container(self): print(" PR description comparison succeed.") @classmethod - def __build_image(cls, docker_file: str, tag: str, cwd: str): + def __build_image(cls, docker_file: str, cwd: str): subprocess.check_call( - ["docker", "build", "--rm", "-f", docker_file, "-t", tag, "."], + ["docker", "build", "--rm", "-f", docker_file, "-t", image_tag, "."], cwd=cwd, )