From 3cb8491d67d65d2262aa1b65091ea9b615b583af Mon Sep 17 00:00:00 2001 From: Chalmer Lowe Date: Wed, 29 Oct 2025 12:04:55 -0400 Subject: [PATCH 01/10] feat: Add Python 3.14 support (#284) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: Add Python 3.14 to noxfile.py * feat: Add Python 3.14 support * feat: Add 3.14 to repo settings * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix: Specify unit_test_python_versions in owlbot.py * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * fix: Update Kokoro presubmit sessions and default Nox Python * fix: Update lint workflow to use Python 3.13 * chore(ci): Set default python to 3.13 for linting * chore(ci): Revert default python to 3.10 for OwlBot compatibility * 🦉 Updates from OwlBot post-processor See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md * chore(ci): Exclude presubmit.cfg from OwlBot management * chore(ci): Restore manually managed presubmit.cfg * Apply suggestion from @chalmerlowe * updates package name for older version of python --------- Co-authored-by: Owl Bot --- .github/sync-repo-settings.yaml | 1 + .github/workflows/lint.yml | 2 +- .github/workflows/unittest.yml | 4 ++-- .kokoro/presubmit/presubmit.cfg | 7 ++++++- noxfile.py | 15 ++++++++------- owlbot.py | 7 ++++++- setup.py | 1 + testing/constraints-3.14.txt | 0 testing/constraints-3.7.txt | 2 +- 9 files changed, 26 insertions(+), 13 deletions(-) create mode 100644 testing/constraints-3.14.txt diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml index 10a8906..259abd5 100644 --- a/.github/sync-repo-settings.yaml +++ b/.github/sync-repo-settings.yaml @@ -17,6 +17,7 @@ branchProtectionRules: - 'unit (3.11)' - 'unit (3.12)' - 'unit (3.13)' + - 'unit (3.14)' - 'cover' permissionRules: - team: actools-python diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 4866193..1051da0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: "3.10" - name: Install nox run: | python -m pip install --upgrade setuptools pip wheel diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 3049ba6..ca36354 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-22.04 strategy: matrix: - python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13'] + python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] steps: - name: Checkout uses: actions/checkout@v4 @@ -45,7 +45,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: "3.8" + python-version: "3.10" - name: Install coverage run: | python -m pip install --upgrade setuptools pip wheel diff --git a/.kokoro/presubmit/presubmit.cfg b/.kokoro/presubmit/presubmit.cfg index 8f43917..f46ff2a 100644 --- a/.kokoro/presubmit/presubmit.cfg +++ b/.kokoro/presubmit/presubmit.cfg @@ -1 +1,6 @@ -# Format: //devtools/kokoro/config/proto/build.proto \ No newline at end of file +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "NOX_SESSION" + value: "blacken mypy check_lower_bounds" +} diff --git a/noxfile.py b/noxfile.py index 7217b33..4828f45 100644 --- a/noxfile.py +++ b/noxfile.py @@ -36,12 +36,13 @@ # Error if a python version is missing nox.options.error_on_missing_interpreters = True +DEFAULT_PYTHON_VERSION = "3.10" BLACK_VERSION = "black==23.7.0" BLACK_PATHS = ["test_utils", "setup.py"] CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() -@nox.session(python="3.8") +@nox.session(python=DEFAULT_PYTHON_VERSION) def lint(session): """Run linters. @@ -57,7 +58,7 @@ def lint(session): session.run("flake8", *BLACK_PATHS) -@nox.session(python="3.8") +@nox.session(python=DEFAULT_PYTHON_VERSION) def blacken(session): """Run black. @@ -70,14 +71,14 @@ def blacken(session): ) -@nox.session(python="3.8") +@nox.session(python=DEFAULT_PYTHON_VERSION) def lint_setup_py(session): """Verify that setup.py is valid (including RST check).""" session.install("docutils", "pygments") session.run("python", "setup.py", "check", "--restructuredtext", "--strict") -@nox.session(python="3.8") +@nox.session(python=DEFAULT_PYTHON_VERSION) def mypy(session): """Verify type hints are mypy compatible.""" session.install("-e", ".") @@ -89,7 +90,7 @@ def mypy(session): session.run("mypy", "test_utils/", "tests/") -@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13"]) +@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]) def unit(session): constraints_path = str( CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" @@ -119,7 +120,7 @@ def unit(session): ) -@nox.session(python="3.8") +@nox.session(python=DEFAULT_PYTHON_VERSION) def check_lower_bounds(session): """Check lower bounds in setup.py are reflected in constraints file""" session.install(".") @@ -133,7 +134,7 @@ def check_lower_bounds(session): ) -@nox.session(python="3.8") +@nox.session(python=DEFAULT_PYTHON_VERSION) def update_lower_bounds(session): """Update lower bounds in constraints.txt to match setup.py""" session.install(".") diff --git a/owlbot.py b/owlbot.py index fdbf918..e720716 100644 --- a/owlbot.py +++ b/owlbot.py @@ -23,7 +23,11 @@ # ---------------------------------------------------------------------------- # Add templated files # ---------------------------------------------------------------------------- -templated_files = common.py_library(cov_level=78) +templated_files = common.py_library( + cov_level=78, + unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"], + default_python_version="3.10", +) s.move( templated_files, excludes=[ @@ -37,6 +41,7 @@ "renovate.json", # no bundle, ignore test resources ".github/workflows/docs.yml", # no docs to publish "README.rst", + ".kokoro/presubmit/presubmit.cfg", # Manually managed ], ) diff --git a/setup.py b/setup.py index e12ad62..9b7b0c9 100644 --- a/setup.py +++ b/setup.py @@ -73,6 +73,7 @@ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Topic :: Internet", "Topic :: Software Development :: Libraries :: Python Modules", ], diff --git a/testing/constraints-3.14.txt b/testing/constraints-3.14.txt new file mode 100644 index 0000000..e69de29 diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt index ed8f59f..e694520 100644 --- a/testing/constraints-3.7.txt +++ b/testing/constraints-3.7.txt @@ -1,4 +1,4 @@ click==7.0.0 google-auth==0.4.0 -importlib-metadata==1.0.0 +importlib_metadata==1.0.0 packaging==19.0 From 19dea9229b7ab4c4b617ea1d7625ddcde05619f7 Mon Sep 17 00:00:00 2001 From: "release-please[bot]" <55107282+release-please[bot]@users.noreply.github.com> Date: Wed, 29 Oct 2025 16:11:40 -0700 Subject: [PATCH 02/10] chore(main): release 1.7.0 (#285) Co-authored-by: release-please[bot] <55107282+release-please[bot]@users.noreply.github.com> --- CHANGELOG.md | 7 +++++++ test_utils/version.py | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f827a7d..92e78e9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## [1.7.0](https://github.com/googleapis/python-test-utils/compare/v1.6.4...v1.7.0) (2025-10-29) + + +### Features + +* Add Python 3.14 support ([#284](https://github.com/googleapis/python-test-utils/issues/284)) ([3cb8491](https://github.com/googleapis/python-test-utils/commit/3cb8491d67d65d2262aa1b65091ea9b615b583af)) + ## [1.6.4](https://github.com/googleapis/python-test-utils/compare/v1.6.3...v1.6.4) (2025-05-19) diff --git a/test_utils/version.py b/test_utils/version.py index c44ef12..3ed5ae3 100644 --- a/test_utils/version.py +++ b/test_utils/version.py @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. # -__version__ = "1.6.4" +__version__ = "1.7.0" From be9fac79f765a2f1b4b7261e9c261570f3625d2a Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Thu, 30 Oct 2025 22:30:34 +0000 Subject: [PATCH 03/10] chore(deps): update actions/checkout action to v5 (#282) Co-authored-by: Daniel Sanche --- .github/workflows/lint.yml | 2 +- .github/workflows/unittest.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 1051da0..adc6178 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Python uses: actions/setup-python@v5 with: diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index ca36354..0c885b5 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -14,7 +14,7 @@ jobs: python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Python uses: actions/setup-python@v5 with: @@ -41,7 +41,7 @@ jobs: - unit steps: - name: Checkout - uses: actions/checkout@v4 + uses: actions/checkout@v5 - name: Setup Python uses: actions/setup-python@v5 with: From 1e80cc8822ba573134bf99f4cd26ff527477977e Mon Sep 17 00:00:00 2001 From: "gcf-owl-bot[bot]" <78513119+gcf-owl-bot[bot]@users.noreply.github.com> Date: Fri, 7 Nov 2025 17:03:21 -0800 Subject: [PATCH 04/10] chore(python): remove configure_previous_major_version_branches (#288) Source-Link: https://github.com/googleapis/synthtool/commit/6702a344265de050bceaff45d62358bb0023ba7d Post-Processor: gcr.io/cloud-devrel-public-resources/owlbot-python:latest@sha256:fbbc8db67afd8b7d71bf694c5081a32da0c528eba166fbcffb3b6e56ddf907d5 --------- Co-authored-by: Owl Bot Co-authored-by: Lingqing Gan --- .github/.OwlBot.lock.yaml | 4 +- .github/workflows/lint.yml | 2 +- .github/workflows/unittest.yml | 4 +- .kokoro/samples/python3.14/common.cfg | 40 ++++++++++++++++++++ .kokoro/samples/python3.14/continuous.cfg | 6 +++ .kokoro/samples/python3.14/periodic-head.cfg | 11 ++++++ .kokoro/samples/python3.14/periodic.cfg | 6 +++ .kokoro/samples/python3.14/presubmit.cfg | 6 +++ 8 files changed, 74 insertions(+), 5 deletions(-) create mode 100644 .kokoro/samples/python3.14/common.cfg create mode 100644 .kokoro/samples/python3.14/continuous.cfg create mode 100644 .kokoro/samples/python3.14/periodic-head.cfg create mode 100644 .kokoro/samples/python3.14/periodic.cfg create mode 100644 .kokoro/samples/python3.14/presubmit.cfg diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml index dd40ef7..0ba6990 100644 --- a/.github/.OwlBot.lock.yaml +++ b/.github/.OwlBot.lock.yaml @@ -13,5 +13,5 @@ # limitations under the License. docker: image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:5581906b957284864632cde4e9c51d1cc66b0094990b27e689132fe5cd036046 -# created: 2025-03-10 + digest: sha256:fbbc8db67afd8b7d71bf694c5081a32da0c528eba166fbcffb3b6e56ddf907d5 +# created: 2025-10-30T00:16:55.473963098Z diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index adc6178..1051da0 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 0c885b5..ca36354 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -14,7 +14,7 @@ jobs: python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: @@ -41,7 +41,7 @@ jobs: - unit steps: - name: Checkout - uses: actions/checkout@v5 + uses: actions/checkout@v4 - name: Setup Python uses: actions/setup-python@v5 with: diff --git a/.kokoro/samples/python3.14/common.cfg b/.kokoro/samples/python3.14/common.cfg new file mode 100644 index 0000000..9f97b8c --- /dev/null +++ b/.kokoro/samples/python3.14/common.cfg @@ -0,0 +1,40 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +# Build logs will be here +action { + define_artifacts { + regex: "**/*sponge_log.xml" + } +} + +# Specify which tests to run +env_vars: { + key: "RUN_TESTS_SESSION" + value: "py-3.14" +} + +# Declare build specific Cloud project. +env_vars: { + key: "BUILD_SPECIFIC_GCLOUD_PROJECT" + value: "python-docs-samples-tests-314" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-test-utils/.kokoro/test-samples.sh" +} + +# Configure the docker image for kokoro-trampoline. +env_vars: { + key: "TRAMPOLINE_IMAGE" + value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" +} + +# Download secrets for samples +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" + +# Download trampoline resources. +gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" + +# Use the trampoline script to run in docker. +build_file: "python-test-utils/.kokoro/trampoline_v2.sh" diff --git a/.kokoro/samples/python3.14/continuous.cfg b/.kokoro/samples/python3.14/continuous.cfg new file mode 100644 index 0000000..a1c8d97 --- /dev/null +++ b/.kokoro/samples/python3.14/continuous.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file diff --git a/.kokoro/samples/python3.14/periodic-head.cfg b/.kokoro/samples/python3.14/periodic-head.cfg new file mode 100644 index 0000000..a36b71a --- /dev/null +++ b/.kokoro/samples/python3.14/periodic-head.cfg @@ -0,0 +1,11 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} + +env_vars: { + key: "TRAMPOLINE_BUILD_FILE" + value: "github/python-test-utils/.kokoro/test-samples-against-head.sh" +} diff --git a/.kokoro/samples/python3.14/periodic.cfg b/.kokoro/samples/python3.14/periodic.cfg new file mode 100644 index 0000000..71cd1e5 --- /dev/null +++ b/.kokoro/samples/python3.14/periodic.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "False" +} diff --git a/.kokoro/samples/python3.14/presubmit.cfg b/.kokoro/samples/python3.14/presubmit.cfg new file mode 100644 index 0000000..a1c8d97 --- /dev/null +++ b/.kokoro/samples/python3.14/presubmit.cfg @@ -0,0 +1,6 @@ +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "INSTALL_LIBRARY_FROM_SOURCE" + value: "True" +} \ No newline at end of file From 0756b7bf33787b00b392c2790748fc717ee70c01 Mon Sep 17 00:00:00 2001 From: ohmayr Date: Wed, 12 Nov 2025 12:08:57 -0800 Subject: [PATCH 05/10] chore(librarian): onboard to librarian (#287) * chore: onboard to librarian * add anchor in changelog * update version * remove old files --------- Co-authored-by: Anthonios Partheniou --- .github/.OwlBot.lock.yaml | 17 ----------- .github/.OwlBot.yaml | 19 ------------ .github/auto-approve.yml | 3 -- .github/release-please.yml | 2 -- .github/release-trigger.yml | 2 -- .github/sync-repo-settings.yaml | 32 --------------------- .librarian/state.yaml | 10 +++++++ CHANGELOG.md | 4 +++ owlbot.py | 51 --------------------------------- 9 files changed, 14 insertions(+), 126 deletions(-) delete mode 100644 .github/.OwlBot.lock.yaml delete mode 100644 .github/.OwlBot.yaml delete mode 100644 .github/auto-approve.yml delete mode 100644 .github/release-please.yml delete mode 100644 .github/release-trigger.yml delete mode 100644 .github/sync-repo-settings.yaml create mode 100644 .librarian/state.yaml delete mode 100644 owlbot.py diff --git a/.github/.OwlBot.lock.yaml b/.github/.OwlBot.lock.yaml deleted file mode 100644 index 0ba6990..0000000 --- a/.github/.OwlBot.lock.yaml +++ /dev/null @@ -1,17 +0,0 @@ -# Copyright 2025 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. -docker: - image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - digest: sha256:fbbc8db67afd8b7d71bf694c5081a32da0c528eba166fbcffb3b6e56ddf907d5 -# created: 2025-10-30T00:16:55.473963098Z diff --git a/.github/.OwlBot.yaml b/.github/.OwlBot.yaml deleted file mode 100644 index 47f10ab..0000000 --- a/.github/.OwlBot.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright 2021 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. - -docker: - image: gcr.io/cloud-devrel-public-resources/owlbot-python:latest - -begin-after-commit-hash: b5412bb83fa37372067353eb46dee44fb8e1eed5 - diff --git a/.github/auto-approve.yml b/.github/auto-approve.yml deleted file mode 100644 index 311ebbb..0000000 --- a/.github/auto-approve.yml +++ /dev/null @@ -1,3 +0,0 @@ -# https://github.com/googleapis/repo-automation-bots/tree/main/packages/auto-approve -processes: - - "OwlBotTemplateChanges" diff --git a/.github/release-please.yml b/.github/release-please.yml deleted file mode 100644 index 466597e..0000000 --- a/.github/release-please.yml +++ /dev/null @@ -1,2 +0,0 @@ -releaseType: python -handleGHRelease: true diff --git a/.github/release-trigger.yml b/.github/release-trigger.yml deleted file mode 100644 index 261d4b2..0000000 --- a/.github/release-trigger.yml +++ /dev/null @@ -1,2 +0,0 @@ -enabled: true -multiScmName: python-test-utils diff --git a/.github/sync-repo-settings.yaml b/.github/sync-repo-settings.yaml deleted file mode 100644 index 259abd5..0000000 --- a/.github/sync-repo-settings.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# https://github.com/googleapis/repo-automation-bots/tree/main/packages/sync-repo-settings -# Rules for main branch protection -branchProtectionRules: -# Identifies the protection rule pattern. Name of the branch to be protected. -# Defaults to `main` -- pattern: main - requiresCodeOwnerReviews: true - requiresStrictStatusChecks: true - requiredStatusCheckContexts: - - 'cla/google' - - 'OwlBot Post Processor' - - 'lint' - - 'unit (3.7)' - - 'unit (3.8)' - - 'unit (3.9)' - - 'unit (3.10)' - - 'unit (3.11)' - - 'unit (3.12)' - - 'unit (3.13)' - - 'unit (3.14)' - - 'cover' -permissionRules: - - team: actools-python - permission: admin - - team: actools - permission: admin - - team: yoshi-python - permission: push - - team: python-samples-owners - permission: push - - team: python-samples-reviewers - permission: push diff --git a/.librarian/state.yaml b/.librarian/state.yaml new file mode 100644 index 0000000..b45c914 --- /dev/null +++ b/.librarian/state.yaml @@ -0,0 +1,10 @@ +image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:c8612d3fffb3f6a32353b2d1abd16b61e87811866f7ec9d65b59b02eb452a620 +libraries: + - id: google-cloud-testutils + version: 1.7.0 + apis: [] + source_roots: + - . + preserve_regex: [] + remove_regex: [] + tag_format: v{version} diff --git a/CHANGELOG.md b/CHANGELOG.md index 92e78e9..75a9655 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +[PyPI History][1] + +[1]: https://pypi.org/project/google-cloud-testutils/#history + ## [1.7.0](https://github.com/googleapis/python-test-utils/compare/v1.6.4...v1.7.0) (2025-10-29) diff --git a/owlbot.py b/owlbot.py deleted file mode 100644 index e720716..0000000 --- a/owlbot.py +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2020 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. - -"""This script is used to synthesize generated parts of this library.""" -import os - -import synthtool as s -import synthtool.gcp as gcp - -common = gcp.CommonTemplates() - -# ---------------------------------------------------------------------------- -# Add templated files -# ---------------------------------------------------------------------------- -templated_files = common.py_library( - cov_level=78, - unit_test_python_versions=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"], - default_python_version="3.10", -) -s.move( - templated_files, - excludes=[ - ".coveragerc", - "MANIFEST.in", # no 'google' package - "noxfile.py", # noxfile is non-standard - "docs/**/*", # no docs to publish - ".kokoro/docs/", - ".kokoro/publish-docs.sh", - "CONTRIBUTING.rst", - "renovate.json", # no bundle, ignore test resources - ".github/workflows/docs.yml", # no docs to publish - "README.rst", - ".kokoro/presubmit/presubmit.cfg", # Manually managed - ], -) - -# Work around bug in templates https://github.com/googleapis/synthtool/pull/1335 -s.replace(".github/workflows/unittest.yml", "--fail-under=100", "--fail-under=78") - -s.shell.run(["nox", "-s", "blacken"], hide_output=False) From beb4fce6792327c456f92f61ab413deeb0857135 Mon Sep 17 00:00:00 2001 From: Daniel Sanche Date: Thu, 20 Nov 2025 14:39:46 -0800 Subject: [PATCH 06/10] chore(tests): update kokoro continuous test config (#303) The continuous kokoro test should use the [same configuration as the pre-submit](https://github.com/googleapis/python-test-utils/blob/main/.kokoro/presubmit/presubmit.cfg) --- .kokoro/continuous/continuous.cfg | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.kokoro/continuous/continuous.cfg b/.kokoro/continuous/continuous.cfg index 8f43917..f46ff2a 100644 --- a/.kokoro/continuous/continuous.cfg +++ b/.kokoro/continuous/continuous.cfg @@ -1 +1,6 @@ -# Format: //devtools/kokoro/config/proto/build.proto \ No newline at end of file +# Format: //devtools/kokoro/config/proto/build.proto + +env_vars: { + key: "NOX_SESSION" + value: "blacken mypy check_lower_bounds" +} From f7542a113c79021e9df4756fb9516e74d4a27305 Mon Sep 17 00:00:00 2001 From: Mend Renovate Date: Tue, 13 Jan 2026 19:15:37 +0000 Subject: [PATCH 07/10] chore(deps): update github artifact actions (major) (#305) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This PR contains the following updates: | Package | Type | Update | Change | |---|---|---|---| | [actions/download-artifact](https://redirect.github.com/actions/download-artifact) | action | major | `v4` → `v7` | | [actions/upload-artifact](https://redirect.github.com/actions/upload-artifact) | action | major | `v4` → `v6` | --- ### Release Notes
actions/download-artifact (actions/download-artifact) ### [`v7`](https://redirect.github.com/actions/download-artifact/compare/v6...v7) [Compare Source](https://redirect.github.com/actions/download-artifact/compare/v6...v7) ### [`v6`](https://redirect.github.com/actions/download-artifact/compare/v5...v6) [Compare Source](https://redirect.github.com/actions/download-artifact/compare/v5...v6) ### [`v5`](https://redirect.github.com/actions/download-artifact/compare/v4...v5) [Compare Source](https://redirect.github.com/actions/download-artifact/compare/v4...v5)
actions/upload-artifact (actions/upload-artifact) ### [`v6`](https://redirect.github.com/actions/upload-artifact/compare/v5...v6) [Compare Source](https://redirect.github.com/actions/upload-artifact/compare/v5...v6) ### [`v5`](https://redirect.github.com/actions/upload-artifact/compare/v4...v5) [Compare Source](https://redirect.github.com/actions/upload-artifact/compare/v4...v5)
--- ### Configuration 📅 **Schedule**: Branch creation - At any time (no schedule defined), Automerge - At any time (no schedule defined). 🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied. ♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox. 👻 **Immortal**: This PR will be recreated if closed unmerged. Get [config help](https://redirect.github.com/renovatebot/renovate/discussions) if that's undesired. --- - [ ] If you want to rebase/retry this PR, check this box --- This PR was generated by [Mend Renovate](https://mend.io/renovate/). View the [repository job log](https://developer.mend.io/github/googleapis/python-test-utils). --- .github/workflows/unittest.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index ca36354..1bd5ecf 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -29,7 +29,7 @@ jobs: run: | nox -s unit-${{ matrix.python }} - name: Upload coverage results - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v6 with: name: coverage-artifact-${{ matrix.python }} path: .coverage-${{ matrix.python }} @@ -51,7 +51,7 @@ jobs: python -m pip install --upgrade setuptools pip wheel python -m pip install coverage - name: Download coverage results - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v7 with: path: .coverage-results/ - name: Report coverage results From 490c505b03f2e249ca46e60bedda1808e1ad57ab Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Fri, 16 Jan 2026 12:16:07 -0500 Subject: [PATCH 08/10] tests: update default Python runtime for tests to 3.14 (#306) --- .github/workflows/lint.yml | 2 +- .github/workflows/unittest.yml | 2 +- noxfile.py | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 1051da0..3ed755f 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -12,7 +12,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: "3.14" - name: Install nox run: | python -m pip install --upgrade setuptools pip wheel diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml index 1bd5ecf..3595911 100644 --- a/.github/workflows/unittest.yml +++ b/.github/workflows/unittest.yml @@ -45,7 +45,7 @@ jobs: - name: Setup Python uses: actions/setup-python@v5 with: - python-version: "3.10" + python-version: "3.14" - name: Install coverage run: | python -m pip install --upgrade setuptools pip wheel diff --git a/noxfile.py b/noxfile.py index 4828f45..d23707d 100644 --- a/noxfile.py +++ b/noxfile.py @@ -36,7 +36,7 @@ # Error if a python version is missing nox.options.error_on_missing_interpreters = True -DEFAULT_PYTHON_VERSION = "3.10" +DEFAULT_PYTHON_VERSION = "3.14" BLACK_VERSION = "black==23.7.0" BLACK_PATHS = ["test_utils", "setup.py"] CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() @@ -74,7 +74,7 @@ def blacken(session): @nox.session(python=DEFAULT_PYTHON_VERSION) def lint_setup_py(session): """Verify that setup.py is valid (including RST check).""" - session.install("docutils", "pygments") + session.install("docutils", "pygments", "setuptools") session.run("python", "setup.py", "check", "--restructuredtext", "--strict") From aeb0690a464bc2d3b66d2b6a18559f7b781b8653 Mon Sep 17 00:00:00 2001 From: gurusai-voleti Date: Mon, 9 Feb 2026 22:22:03 +0530 Subject: [PATCH 09/10] chore: Migrate gsutil usage to gcloud storage (#307) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Automated: Migrate {target_path} from gsutil to gcloud storage This CL is part of the on going effort to migrate from the legacy `gsutil` tool to the new and improved `gcloud storage` command-line interface. `gcloud storage` is the recommended and modern tool for interacting with Google Cloud Storage, offering better performance, unified authentication, and a more consistent command structure with other `gcloud` components. 🚀 ### Automation Details This change was **generated automatically** by an agent that targets users of `gsutil`. The transformations applied are based on the [gsutil to gcloud storage migration guide](http://go/gsutil-gcloud-storage-migration-guide). ### ⚠️ Action Required: Please Review and Test Carefully While we have based the automation on the migration guide, every use case is unique. **It is crucial that you thoroughly test these changes in environments appropriate to your use-case before merging.** Be aware of potential differences between `gsutil` and `gcloud storage` that could impact your workflows. For instance, the structure of command output may have changed, requiring updates to any scripts that parse it. Similarly, command behavior can differ subtly; the `gcloud storage rsync` command has a different file deletion logic than `gsutil rsync`, which could lead to unintended file deletions. Our migration guides can help guide you through a list of mappings and some notable differences between the two tools. Standard presubmit tests are run as part of this CL's workflow. **If you need to target an additional test workflow or require assistance with testing, please let us know.** Please verify that all your Cloud Storage operations continue to work as expected to avoid any potential disruptions in production. ### Support and Collaboration The `GCS CLI` team is here to help! If you encounter any issues, have a complex use case that this automated change doesn't cover, or face any other blockers, please don't hesitate to reach out. We are happy to work with you to test and adjust these changes as needed. **Contact:** `gcs-cli-hyd@google.com` We appreciate your partnership in this important migration effort! #gsutil-migration --- .kokoro/trampoline_v2.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh index 35fa529..d03f92d 100755 --- a/.kokoro/trampoline_v2.sh +++ b/.kokoro/trampoline_v2.sh @@ -26,8 +26,8 @@ # To run this script, first download few files from gcs to /dev/shm. # (/dev/shm is passed into the container as KOKORO_GFILE_DIR). # -# gsutil cp gs://cloud-devrel-kokoro-resources/python-docs-samples/secrets_viewer_service_account.json /dev/shm -# gsutil cp gs://cloud-devrel-kokoro-resources/python-docs-samples/automl_secrets.txt /dev/shm +# gcloud storage cp gs://cloud-devrel-kokoro-resources/python-docs-samples/secrets_viewer_service_account.json /dev/shm +# gcloud storage cp gs://cloud-devrel-kokoro-resources/python-docs-samples/automl_secrets.txt /dev/shm # # Then run the script. # .kokoro/trampoline_v2.sh From 987b92a0cce002abe7e65f03dbcbefe9bd8d3049 Mon Sep 17 00:00:00 2001 From: Anthonios Partheniou Date: Mon, 9 Feb 2026 18:11:30 -0500 Subject: [PATCH 10/10] build: update README to indicate that source has moved and delete all files (#309) Towards https://github.com/googleapis/google-cloud-python/issues/10960. --- .flake8 | 33 -- .github/CODEOWNERS | 12 - .github/CONTRIBUTING.md | 28 - .github/ISSUE_TEMPLATE/bug_report.md | 43 -- .github/ISSUE_TEMPLATE/feature_request.md | 18 - .github/ISSUE_TEMPLATE/support_request.md | 7 - .github/PULL_REQUEST_TEMPLATE.md | 7 - .github/auto-label.yaml | 20 - .github/blunderbuss.yml | 17 - .github/header-checker-lint.yml | 15 - .github/snippet-bot.yml | 0 .github/workflows/lint.yml | 25 - .github/workflows/unittest.yml | 61 --- .gitignore | 64 --- .kokoro/build.sh | 60 --- .kokoro/continuous/common.cfg | 27 - .kokoro/continuous/continuous.cfg | 6 - .kokoro/continuous/prerelease-deps.cfg | 7 - .kokoro/populate-secrets.sh | 43 -- .kokoro/presubmit/common.cfg | 27 - .kokoro/presubmit/prerelease-deps.cfg | 7 - .kokoro/presubmit/presubmit.cfg | 6 - .kokoro/samples/lint/common.cfg | 34 -- .kokoro/samples/lint/continuous.cfg | 6 - .kokoro/samples/lint/periodic.cfg | 6 - .kokoro/samples/lint/presubmit.cfg | 6 - .kokoro/samples/python3.10/common.cfg | 40 -- .kokoro/samples/python3.10/continuous.cfg | 6 - .kokoro/samples/python3.10/periodic-head.cfg | 11 - .kokoro/samples/python3.10/periodic.cfg | 6 - .kokoro/samples/python3.10/presubmit.cfg | 6 - .kokoro/samples/python3.11/common.cfg | 40 -- .kokoro/samples/python3.11/continuous.cfg | 6 - .kokoro/samples/python3.11/periodic-head.cfg | 11 - .kokoro/samples/python3.11/periodic.cfg | 6 - .kokoro/samples/python3.11/presubmit.cfg | 6 - .kokoro/samples/python3.12/common.cfg | 40 -- .kokoro/samples/python3.12/continuous.cfg | 6 - .kokoro/samples/python3.12/periodic-head.cfg | 11 - .kokoro/samples/python3.12/periodic.cfg | 6 - .kokoro/samples/python3.12/presubmit.cfg | 6 - .kokoro/samples/python3.13/common.cfg | 40 -- .kokoro/samples/python3.13/continuous.cfg | 6 - .kokoro/samples/python3.13/periodic-head.cfg | 11 - .kokoro/samples/python3.13/periodic.cfg | 6 - .kokoro/samples/python3.13/presubmit.cfg | 6 - .kokoro/samples/python3.14/common.cfg | 40 -- .kokoro/samples/python3.14/continuous.cfg | 6 - .kokoro/samples/python3.14/periodic-head.cfg | 11 - .kokoro/samples/python3.14/periodic.cfg | 6 - .kokoro/samples/python3.14/presubmit.cfg | 6 - .kokoro/samples/python3.7/common.cfg | 40 -- .kokoro/samples/python3.7/continuous.cfg | 6 - .kokoro/samples/python3.7/periodic-head.cfg | 11 - .kokoro/samples/python3.7/periodic.cfg | 6 - .kokoro/samples/python3.7/presubmit.cfg | 6 - .kokoro/samples/python3.8/common.cfg | 40 -- .kokoro/samples/python3.8/continuous.cfg | 6 - .kokoro/samples/python3.8/periodic-head.cfg | 11 - .kokoro/samples/python3.8/periodic.cfg | 6 - .kokoro/samples/python3.8/presubmit.cfg | 6 - .kokoro/samples/python3.9/common.cfg | 40 -- .kokoro/samples/python3.9/continuous.cfg | 6 - .kokoro/samples/python3.9/periodic-head.cfg | 11 - .kokoro/samples/python3.9/periodic.cfg | 6 - .kokoro/samples/python3.9/presubmit.cfg | 6 - .kokoro/test-samples-against-head.sh | 26 - .kokoro/test-samples-impl.sh | 103 ---- .kokoro/test-samples.sh | 44 -- .kokoro/trampoline.sh | 28 - .kokoro/trampoline_v2.sh | 487 ------------------ .librarian/state.yaml | 10 - .pre-commit-config.yaml | 31 -- .trampolinerc | 61 --- CHANGELOG.md | 131 ----- CODE_OF_CONDUCT.md | 95 ---- MANIFEST.in | 25 - README.rst | 5 + mypy.ini | 3 - noxfile.py | 148 ------ pytest.ini | 4 - renovate.json | 19 - scripts/decrypt-secrets.sh | 46 -- scripts/readme-gen/readme_gen.py | 69 --- scripts/readme-gen/templates/README.tmpl.rst | 87 ---- scripts/readme-gen/templates/auth.tmpl.rst | 9 - .../templates/auth_api_key.tmpl.rst | 14 - .../templates/install_deps.tmpl.rst | 29 -- .../templates/install_portaudio.tmpl.rst | 35 -- setup.py | 81 --- test_utils/__init__.py | 16 - test_utils/imports.py | 38 -- test_utils/lower_bound_checker/__init__.py | 0 .../lower_bound_checker.py | 271 ---------- test_utils/orchestrate.py | 446 ---------------- test_utils/prefixer.py | 82 --- test_utils/py.typed | 2 - test_utils/retry.py | 228 -------- test_utils/system.py | 80 --- test_utils/version.py | 15 - test_utils/vpcsc_config.py | 118 ----- testing/.gitignore | 3 - testing/constraints-3.10.txt | 0 testing/constraints-3.11.txt | 0 testing/constraints-3.12.txt | 0 testing/constraints-3.13.txt | 0 testing/constraints-3.14.txt | 0 testing/constraints-3.7.txt | 4 - testing/constraints-3.8.txt | 0 testing/constraints-3.9.txt | 0 tests/unit/resources/bad_package/setup.py | 40 -- tests/unit/resources/good_package/setup.py | 46 -- tests/unit/test_lower_bound_checker.py | 314 ----------- tests/unit/test_orchestrate.py | 380 -------------- tests/unit/test_prefixer.py | 89 ---- 115 files changed, 5 insertions(+), 4894 deletions(-) delete mode 100644 .flake8 delete mode 100644 .github/CODEOWNERS delete mode 100644 .github/CONTRIBUTING.md delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md delete mode 100644 .github/ISSUE_TEMPLATE/support_request.md delete mode 100644 .github/PULL_REQUEST_TEMPLATE.md delete mode 100644 .github/auto-label.yaml delete mode 100644 .github/blunderbuss.yml delete mode 100644 .github/header-checker-lint.yml delete mode 100644 .github/snippet-bot.yml delete mode 100644 .github/workflows/lint.yml delete mode 100644 .github/workflows/unittest.yml delete mode 100644 .gitignore delete mode 100755 .kokoro/build.sh delete mode 100644 .kokoro/continuous/common.cfg delete mode 100644 .kokoro/continuous/continuous.cfg delete mode 100644 .kokoro/continuous/prerelease-deps.cfg delete mode 100755 .kokoro/populate-secrets.sh delete mode 100644 .kokoro/presubmit/common.cfg delete mode 100644 .kokoro/presubmit/prerelease-deps.cfg delete mode 100644 .kokoro/presubmit/presubmit.cfg delete mode 100644 .kokoro/samples/lint/common.cfg delete mode 100644 .kokoro/samples/lint/continuous.cfg delete mode 100644 .kokoro/samples/lint/periodic.cfg delete mode 100644 .kokoro/samples/lint/presubmit.cfg delete mode 100644 .kokoro/samples/python3.10/common.cfg delete mode 100644 .kokoro/samples/python3.10/continuous.cfg delete mode 100644 .kokoro/samples/python3.10/periodic-head.cfg delete mode 100644 .kokoro/samples/python3.10/periodic.cfg delete mode 100644 .kokoro/samples/python3.10/presubmit.cfg delete mode 100644 .kokoro/samples/python3.11/common.cfg delete mode 100644 .kokoro/samples/python3.11/continuous.cfg delete mode 100644 .kokoro/samples/python3.11/periodic-head.cfg delete mode 100644 .kokoro/samples/python3.11/periodic.cfg delete mode 100644 .kokoro/samples/python3.11/presubmit.cfg delete mode 100644 .kokoro/samples/python3.12/common.cfg delete mode 100644 .kokoro/samples/python3.12/continuous.cfg delete mode 100644 .kokoro/samples/python3.12/periodic-head.cfg delete mode 100644 .kokoro/samples/python3.12/periodic.cfg delete mode 100644 .kokoro/samples/python3.12/presubmit.cfg delete mode 100644 .kokoro/samples/python3.13/common.cfg delete mode 100644 .kokoro/samples/python3.13/continuous.cfg delete mode 100644 .kokoro/samples/python3.13/periodic-head.cfg delete mode 100644 .kokoro/samples/python3.13/periodic.cfg delete mode 100644 .kokoro/samples/python3.13/presubmit.cfg delete mode 100644 .kokoro/samples/python3.14/common.cfg delete mode 100644 .kokoro/samples/python3.14/continuous.cfg delete mode 100644 .kokoro/samples/python3.14/periodic-head.cfg delete mode 100644 .kokoro/samples/python3.14/periodic.cfg delete mode 100644 .kokoro/samples/python3.14/presubmit.cfg delete mode 100644 .kokoro/samples/python3.7/common.cfg delete mode 100644 .kokoro/samples/python3.7/continuous.cfg delete mode 100644 .kokoro/samples/python3.7/periodic-head.cfg delete mode 100644 .kokoro/samples/python3.7/periodic.cfg delete mode 100644 .kokoro/samples/python3.7/presubmit.cfg delete mode 100644 .kokoro/samples/python3.8/common.cfg delete mode 100644 .kokoro/samples/python3.8/continuous.cfg delete mode 100644 .kokoro/samples/python3.8/periodic-head.cfg delete mode 100644 .kokoro/samples/python3.8/periodic.cfg delete mode 100644 .kokoro/samples/python3.8/presubmit.cfg delete mode 100644 .kokoro/samples/python3.9/common.cfg delete mode 100644 .kokoro/samples/python3.9/continuous.cfg delete mode 100644 .kokoro/samples/python3.9/periodic-head.cfg delete mode 100644 .kokoro/samples/python3.9/periodic.cfg delete mode 100644 .kokoro/samples/python3.9/presubmit.cfg delete mode 100755 .kokoro/test-samples-against-head.sh delete mode 100755 .kokoro/test-samples-impl.sh delete mode 100755 .kokoro/test-samples.sh delete mode 100755 .kokoro/trampoline.sh delete mode 100755 .kokoro/trampoline_v2.sh delete mode 100644 .librarian/state.yaml delete mode 100644 .pre-commit-config.yaml delete mode 100644 .trampolinerc delete mode 100644 CHANGELOG.md delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 MANIFEST.in delete mode 100644 mypy.ini delete mode 100644 noxfile.py delete mode 100644 pytest.ini delete mode 100644 renovate.json delete mode 100755 scripts/decrypt-secrets.sh delete mode 100644 scripts/readme-gen/readme_gen.py delete mode 100644 scripts/readme-gen/templates/README.tmpl.rst delete mode 100644 scripts/readme-gen/templates/auth.tmpl.rst delete mode 100644 scripts/readme-gen/templates/auth_api_key.tmpl.rst delete mode 100644 scripts/readme-gen/templates/install_deps.tmpl.rst delete mode 100644 scripts/readme-gen/templates/install_portaudio.tmpl.rst delete mode 100644 setup.py delete mode 100644 test_utils/__init__.py delete mode 100644 test_utils/imports.py delete mode 100644 test_utils/lower_bound_checker/__init__.py delete mode 100644 test_utils/lower_bound_checker/lower_bound_checker.py delete mode 100644 test_utils/orchestrate.py delete mode 100644 test_utils/prefixer.py delete mode 100644 test_utils/py.typed delete mode 100644 test_utils/retry.py delete mode 100644 test_utils/system.py delete mode 100644 test_utils/version.py delete mode 100644 test_utils/vpcsc_config.py delete mode 100644 testing/.gitignore delete mode 100644 testing/constraints-3.10.txt delete mode 100644 testing/constraints-3.11.txt delete mode 100644 testing/constraints-3.12.txt delete mode 100644 testing/constraints-3.13.txt delete mode 100644 testing/constraints-3.14.txt delete mode 100644 testing/constraints-3.7.txt delete mode 100644 testing/constraints-3.8.txt delete mode 100644 testing/constraints-3.9.txt delete mode 100644 tests/unit/resources/bad_package/setup.py delete mode 100644 tests/unit/resources/good_package/setup.py delete mode 100644 tests/unit/test_lower_bound_checker.py delete mode 100644 tests/unit/test_orchestrate.py delete mode 100644 tests/unit/test_prefixer.py diff --git a/.flake8 b/.flake8 deleted file mode 100644 index 32986c7..0000000 --- a/.flake8 +++ /dev/null @@ -1,33 +0,0 @@ -# -*- coding: utf-8 -*- -# -# 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. - -# Generated by synthtool. DO NOT EDIT! -[flake8] -ignore = E203, E231, E266, E501, W503 -exclude = - # Exclude generated code. - **/proto/** - **/gapic/** - **/services/** - **/types/** - *_pb2.py - - # Standard linting exemptions. - **/.nox/** - __pycache__, - .git, - *.pyc, - conf.py diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS deleted file mode 100644 index e446644..0000000 --- a/.github/CODEOWNERS +++ /dev/null @@ -1,12 +0,0 @@ -# Code owners file. -# This file controls who is tagged for review for any given pull request. -# -# For syntax help see: -# https://help.github.com/en/github/creating-cloning-and-archiving-repositories/about-code-owners#codeowners-syntax -# Note: This file is autogenerated. To make changes to the codeowner team, please update .repo-metadata.json. - -# @googleapis/yoshi-python is the default owner for changes in this repo -* @googleapis/yoshi-python - -# @googleapis/python-samples-reviewers is the default owner for samples changes -/samples/ @googleapis/python-samples-reviewers diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md deleted file mode 100644 index 939e534..0000000 --- a/.github/CONTRIBUTING.md +++ /dev/null @@ -1,28 +0,0 @@ -# How to Contribute - -We'd love to accept your patches and contributions to this project. There are -just a few small guidelines you need to follow. - -## Contributor License Agreement - -Contributions to this project must be accompanied by a Contributor License -Agreement. You (or your employer) retain the copyright to your contribution; -this simply gives us permission to use and redistribute your contributions as -part of the project. Head over to to see -your current agreements on file or to sign a new one. - -You generally only need to submit a CLA once, so if you've already submitted one -(even if it was for a different project), you probably don't need to do it -again. - -## Code reviews - -All submissions, including submissions by project members, require review. We -use GitHub pull requests for this purpose. Consult -[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more -information on using pull requests. - -## Community Guidelines - -This project follows [Google's Open Source Community -Guidelines](https://opensource.google.com/conduct/). diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index e2ed8a2..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,43 +0,0 @@ ---- -name: Bug report -about: Create a report to help us improve - ---- - -Thanks for stopping by to let us know something could be better! - -**PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response. - -Please run down the following list and make sure you've tried the usual "quick fixes": - - - Search the issues already opened: https://github.com/googleapis/python-test-utils/issues - - Search StackOverflow: https://stackoverflow.com/questions/tagged/google-cloud-platform+python - -If you are still having issues, please be sure to include as much information as possible: - -#### Environment details - - - OS type and version: - - Python version: `python --version` - - pip version: `pip --version` - - `google-cloud-testutils` version: `pip show google-cloud-testutils` - -#### Steps to reproduce - - 1. ? - 2. ? - -#### Code example - -```python -# example -``` - -#### Stack trace -``` -# example -``` - -Making sure to follow these steps will guarantee the quickest resolution possible. - -Thanks! diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index 6365857..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,18 +0,0 @@ ---- -name: Feature request -about: Suggest an idea for this library - ---- - -Thanks for stopping by to let us know something could be better! - -**PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response. - - **Is your feature request related to a problem? Please describe.** -A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] - **Describe the solution you'd like** -A clear and concise description of what you want to happen. - **Describe alternatives you've considered** -A clear and concise description of any alternative solutions or features you've considered. - **Additional context** -Add any other context or screenshots about the feature request here. diff --git a/.github/ISSUE_TEMPLATE/support_request.md b/.github/ISSUE_TEMPLATE/support_request.md deleted file mode 100644 index 9958690..0000000 --- a/.github/ISSUE_TEMPLATE/support_request.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -name: Support request -about: If you have a support contract with Google, please create an issue in the Google Cloud Support console. - ---- - -**PLEASE READ**: If you have a support contract with Google, please create an issue in the [support console](https://cloud.google.com/support/) instead of filing on GitHub. This will ensure a timely response. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md deleted file mode 100644 index aa6c307..0000000 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ /dev/null @@ -1,7 +0,0 @@ -Thank you for opening a Pull Request! Before submitting your PR, there are a few things you can do to make sure it goes smoothly: -- [ ] Make sure to open an issue as a [bug/issue](https://github.com/googleapis/python-test-utils/issues/new/choose) before writing your code! That way we can discuss the change, evaluate designs, and agree on the general idea -- [ ] Ensure the tests and linter pass -- [ ] Code coverage does not decrease (if any source code was changed) -- [ ] Appropriate docs were updated (if necessary) - -Fixes # 🦕 diff --git a/.github/auto-label.yaml b/.github/auto-label.yaml deleted file mode 100644 index 21786a4..0000000 --- a/.github/auto-label.yaml +++ /dev/null @@ -1,20 +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. -requestsize: - enabled: true - -path: - pullrequest: true - paths: - samples: "samples" diff --git a/.github/blunderbuss.yml b/.github/blunderbuss.yml deleted file mode 100644 index 990b311..0000000 --- a/.github/blunderbuss.yml +++ /dev/null @@ -1,17 +0,0 @@ -# Blunderbuss config -# -# This file controls who is assigned for pull requests and issues. -# Note: This file is autogenerated. To make changes to the assignee -# team, please update `codeowner_team` in `.repo-metadata.json`. - -assign_issues: - - googleapis/python-core-client-libraries - -assign_issues_by: - - labels: - - "samples" - to: - - googleapis/python-samples-reviewers - -assign_prs: - - googleapis/python-core-client-libraries diff --git a/.github/header-checker-lint.yml b/.github/header-checker-lint.yml deleted file mode 100644 index 6fe78aa..0000000 --- a/.github/header-checker-lint.yml +++ /dev/null @@ -1,15 +0,0 @@ -{"allowedCopyrightHolders": ["Google LLC"], - "allowedLicenses": ["Apache-2.0", "MIT", "BSD-3"], - "ignoreFiles": ["**/requirements.txt", "**/requirements-test.txt", "**/__init__.py", "samples/**/constraints.txt", "samples/**/constraints-test.txt"], - "sourceFileExtensions": [ - "ts", - "js", - "java", - "sh", - "Dockerfile", - "yaml", - "py", - "html", - "txt" - ] -} \ No newline at end of file diff --git a/.github/snippet-bot.yml b/.github/snippet-bot.yml deleted file mode 100644 index e69de29..0000000 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml deleted file mode 100644 index 3ed755f..0000000 --- a/.github/workflows/lint.yml +++ /dev/null @@ -1,25 +0,0 @@ -on: - pull_request: - branches: - - main -name: lint -jobs: - lint: - runs-on: ubuntu-latest - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - name: Install nox - run: | - python -m pip install --upgrade setuptools pip wheel - python -m pip install nox - - name: Run lint - run: | - nox -s lint - - name: Run lint_setup_py - run: | - nox -s lint_setup_py diff --git a/.github/workflows/unittest.yml b/.github/workflows/unittest.yml deleted file mode 100644 index 3595911..0000000 --- a/.github/workflows/unittest.yml +++ /dev/null @@ -1,61 +0,0 @@ -on: - pull_request: - branches: - - main -name: unittest -jobs: - unit: - # TODO(https://github.com/googleapis/gapic-generator-python/issues/2303): use `ubuntu-latest` once this bug is fixed. - # Use ubuntu-22.04 until Python 3.7 is removed from the test matrix - # https://docs.github.com/en/actions/using-github-hosted-runners/using-github-hosted-runners/about-github-hosted-runners#standard-github-hosted-runners-for-public-repositories - runs-on: ubuntu-22.04 - strategy: - matrix: - python: ['3.7', '3.8', '3.9', '3.10', '3.11', '3.12', '3.13', '3.14'] - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python }} - - name: Install nox - run: | - python -m pip install --upgrade setuptools pip wheel - python -m pip install nox - - name: Run unit tests - env: - COVERAGE_FILE: .coverage-${{ matrix.python }} - run: | - nox -s unit-${{ matrix.python }} - - name: Upload coverage results - uses: actions/upload-artifact@v6 - with: - name: coverage-artifact-${{ matrix.python }} - path: .coverage-${{ matrix.python }} - include-hidden-files: true - - cover: - runs-on: ubuntu-latest - needs: - - unit - steps: - - name: Checkout - uses: actions/checkout@v4 - - name: Setup Python - uses: actions/setup-python@v5 - with: - python-version: "3.14" - - name: Install coverage - run: | - python -m pip install --upgrade setuptools pip wheel - python -m pip install coverage - - name: Download coverage results - uses: actions/download-artifact@v7 - with: - path: .coverage-results/ - - name: Report coverage results - run: | - find .coverage-results -type f -name '*.zip' -exec unzip {} \; - coverage combine .coverage-results/**/.coverage* - coverage report --show-missing --fail-under=78 diff --git a/.gitignore b/.gitignore deleted file mode 100644 index d083ea1..0000000 --- a/.gitignore +++ /dev/null @@ -1,64 +0,0 @@ -*.py[cod] -*.sw[op] - -# C extensions -*.so - -# Packages -*.egg -*.egg-info -dist -build -eggs -.eggs -parts -bin -var -sdist -develop-eggs -.installed.cfg -lib -lib64 -__pycache__ - -# Installer logs -pip-log.txt - -# Unit test / coverage reports -.coverage -.nox -.cache -.pytest_cache - - -# Mac -.DS_Store - -# JetBrains -.idea - -# VS Code -.vscode - -# emacs -*~ - -# Built documentation -docs/_build -bigquery/docs/generated -docs.metadata - -# Virtual environment -env/ -venv/ - -# Test logs -coverage.xml -*sponge_log.xml - -# System test environment variables. -system_tests/local_test_setup - -# Make sure a generated file isn't accidentally committed. -pylintrc -pylintrc.test diff --git a/.kokoro/build.sh b/.kokoro/build.sh deleted file mode 100755 index d41b45a..0000000 --- a/.kokoro/build.sh +++ /dev/null @@ -1,60 +0,0 @@ -#!/bin/bash -# 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. - -set -eo pipefail - -CURRENT_DIR=$(dirname "${BASH_SOURCE[0]}") - -if [[ -z "${PROJECT_ROOT:-}" ]]; then - PROJECT_ROOT=$(realpath "${CURRENT_DIR}/..") -fi - -pushd "${PROJECT_ROOT}" - -# Disable buffering, so that the logs stream through. -export PYTHONUNBUFFERED=1 - -# Debug: show build environment -env | grep KOKORO - -# Setup service account credentials. -if [[ -f "${KOKORO_GFILE_DIR}/service-account.json" ]] -then - export GOOGLE_APPLICATION_CREDENTIALS=${KOKORO_GFILE_DIR}/service-account.json -fi - -# Setup project id. -if [[ -f "${KOKORO_GFILE_DIR}/project-id.json" ]] -then - export PROJECT_ID=$(cat "${KOKORO_GFILE_DIR}/project-id.json") -fi - -# If this is a continuous build, send the test log to the FlakyBot. -# See https://github.com/googleapis/repo-automation-bots/tree/main/packages/flakybot. -if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"continuous"* ]]; then - cleanup() { - chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot - $KOKORO_GFILE_DIR/linux_amd64/flakybot - } - trap cleanup EXIT HUP -fi - -# If NOX_SESSION is set, it only runs the specified session, -# otherwise run all the sessions. -if [[ -n "${NOX_SESSION:-}" ]]; then - python3 -m nox -s ${NOX_SESSION:-} -else - python3 -m nox -fi diff --git a/.kokoro/continuous/common.cfg b/.kokoro/continuous/common.cfg deleted file mode 100644 index cad4e82..0000000 --- a/.kokoro/continuous/common.cfg +++ /dev/null @@ -1,27 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Download resources for system tests (service account key, etc.) -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/google-cloud-python" - -# Use the trampoline script to run in docker. -build_file: "python-test-utils/.kokoro/trampoline.sh" - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-multi" -} -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/build.sh" -} diff --git a/.kokoro/continuous/continuous.cfg b/.kokoro/continuous/continuous.cfg deleted file mode 100644 index f46ff2a..0000000 --- a/.kokoro/continuous/continuous.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "NOX_SESSION" - value: "blacken mypy check_lower_bounds" -} diff --git a/.kokoro/continuous/prerelease-deps.cfg b/.kokoro/continuous/prerelease-deps.cfg deleted file mode 100644 index 3595fb4..0000000 --- a/.kokoro/continuous/prerelease-deps.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Only run this nox session. -env_vars: { - key: "NOX_SESSION" - value: "prerelease_deps" -} diff --git a/.kokoro/populate-secrets.sh b/.kokoro/populate-secrets.sh deleted file mode 100755 index c435402..0000000 --- a/.kokoro/populate-secrets.sh +++ /dev/null @@ -1,43 +0,0 @@ -#!/bin/bash -# 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. - -set -eo pipefail - -function now { date +"%Y-%m-%d %H:%M:%S" | tr -d '\n' ;} -function msg { println "$*" >&2 ;} -function println { printf '%s\n' "$(now) $*" ;} - - -# Populates requested secrets set in SECRET_MANAGER_KEYS from service account: -# kokoro-trampoline@cloud-devrel-kokoro-resources.iam.gserviceaccount.com -SECRET_LOCATION="${KOKORO_GFILE_DIR}/secret_manager" -msg "Creating folder on disk for secrets: ${SECRET_LOCATION}" -mkdir -p ${SECRET_LOCATION} -for key in $(echo ${SECRET_MANAGER_KEYS} | sed "s/,/ /g") -do - msg "Retrieving secret ${key}" - docker run --entrypoint=gcloud \ - --volume=${KOKORO_GFILE_DIR}:${KOKORO_GFILE_DIR} \ - gcr.io/google.com/cloudsdktool/cloud-sdk \ - secrets versions access latest \ - --project cloud-devrel-kokoro-resources \ - --secret ${key} > \ - "${SECRET_LOCATION}/${key}" - if [[ $? == 0 ]]; then - msg "Secret written to ${SECRET_LOCATION}/${key}" - else - msg "Error retrieving secret ${key}" - fi -done diff --git a/.kokoro/presubmit/common.cfg b/.kokoro/presubmit/common.cfg deleted file mode 100644 index cad4e82..0000000 --- a/.kokoro/presubmit/common.cfg +++ /dev/null @@ -1,27 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Download resources for system tests (service account key, etc.) -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/google-cloud-python" - -# Use the trampoline script to run in docker. -build_file: "python-test-utils/.kokoro/trampoline.sh" - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-multi" -} -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/build.sh" -} diff --git a/.kokoro/presubmit/prerelease-deps.cfg b/.kokoro/presubmit/prerelease-deps.cfg deleted file mode 100644 index 3595fb4..0000000 --- a/.kokoro/presubmit/prerelease-deps.cfg +++ /dev/null @@ -1,7 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Only run this nox session. -env_vars: { - key: "NOX_SESSION" - value: "prerelease_deps" -} diff --git a/.kokoro/presubmit/presubmit.cfg b/.kokoro/presubmit/presubmit.cfg deleted file mode 100644 index f46ff2a..0000000 --- a/.kokoro/presubmit/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "NOX_SESSION" - value: "blacken mypy check_lower_bounds" -} diff --git a/.kokoro/samples/lint/common.cfg b/.kokoro/samples/lint/common.cfg deleted file mode 100644 index 1b4228c..0000000 --- a/.kokoro/samples/lint/common.cfg +++ /dev/null @@ -1,34 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Specify which tests to run -env_vars: { - key: "RUN_TESTS_SESSION" - value: "lint" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples.sh" -} - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" -} - -# Download secrets for samples -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "python-test-utils/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/lint/continuous.cfg b/.kokoro/samples/lint/continuous.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/lint/continuous.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/lint/periodic.cfg b/.kokoro/samples/lint/periodic.cfg deleted file mode 100644 index 50fec96..0000000 --- a/.kokoro/samples/lint/periodic.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "False" -} \ No newline at end of file diff --git a/.kokoro/samples/lint/presubmit.cfg b/.kokoro/samples/lint/presubmit.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/lint/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.10/common.cfg b/.kokoro/samples/python3.10/common.cfg deleted file mode 100644 index f645373..0000000 --- a/.kokoro/samples/python3.10/common.cfg +++ /dev/null @@ -1,40 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Specify which tests to run -env_vars: { - key: "RUN_TESTS_SESSION" - value: "py-3.10" -} - -# Declare build specific Cloud project. -env_vars: { - key: "BUILD_SPECIFIC_GCLOUD_PROJECT" - value: "python-docs-samples-tests-310" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples.sh" -} - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" -} - -# Download secrets for samples -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "python-test-utils/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.10/continuous.cfg b/.kokoro/samples/python3.10/continuous.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.10/continuous.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.10/periodic-head.cfg b/.kokoro/samples/python3.10/periodic-head.cfg deleted file mode 100644 index a36b71a..0000000 --- a/.kokoro/samples/python3.10/periodic-head.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples-against-head.sh" -} diff --git a/.kokoro/samples/python3.10/periodic.cfg b/.kokoro/samples/python3.10/periodic.cfg deleted file mode 100644 index 71cd1e5..0000000 --- a/.kokoro/samples/python3.10/periodic.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "False" -} diff --git a/.kokoro/samples/python3.10/presubmit.cfg b/.kokoro/samples/python3.10/presubmit.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.10/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.11/common.cfg b/.kokoro/samples/python3.11/common.cfg deleted file mode 100644 index 4b6b264..0000000 --- a/.kokoro/samples/python3.11/common.cfg +++ /dev/null @@ -1,40 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Specify which tests to run -env_vars: { - key: "RUN_TESTS_SESSION" - value: "py-3.11" -} - -# Declare build specific Cloud project. -env_vars: { - key: "BUILD_SPECIFIC_GCLOUD_PROJECT" - value: "python-docs-samples-tests-311" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples.sh" -} - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" -} - -# Download secrets for samples -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "python-test-utils/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.11/continuous.cfg b/.kokoro/samples/python3.11/continuous.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.11/continuous.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.11/periodic-head.cfg b/.kokoro/samples/python3.11/periodic-head.cfg deleted file mode 100644 index a36b71a..0000000 --- a/.kokoro/samples/python3.11/periodic-head.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples-against-head.sh" -} diff --git a/.kokoro/samples/python3.11/periodic.cfg b/.kokoro/samples/python3.11/periodic.cfg deleted file mode 100644 index 71cd1e5..0000000 --- a/.kokoro/samples/python3.11/periodic.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "False" -} diff --git a/.kokoro/samples/python3.11/presubmit.cfg b/.kokoro/samples/python3.11/presubmit.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.11/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.12/common.cfg b/.kokoro/samples/python3.12/common.cfg deleted file mode 100644 index 078e72d..0000000 --- a/.kokoro/samples/python3.12/common.cfg +++ /dev/null @@ -1,40 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Specify which tests to run -env_vars: { - key: "RUN_TESTS_SESSION" - value: "py-3.12" -} - -# Declare build specific Cloud project. -env_vars: { - key: "BUILD_SPECIFIC_GCLOUD_PROJECT" - value: "python-docs-samples-tests-312" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples.sh" -} - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" -} - -# Download secrets for samples -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "python-test-utils/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.12/continuous.cfg b/.kokoro/samples/python3.12/continuous.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.12/continuous.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.12/periodic-head.cfg b/.kokoro/samples/python3.12/periodic-head.cfg deleted file mode 100644 index a36b71a..0000000 --- a/.kokoro/samples/python3.12/periodic-head.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples-against-head.sh" -} diff --git a/.kokoro/samples/python3.12/periodic.cfg b/.kokoro/samples/python3.12/periodic.cfg deleted file mode 100644 index 71cd1e5..0000000 --- a/.kokoro/samples/python3.12/periodic.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "False" -} diff --git a/.kokoro/samples/python3.12/presubmit.cfg b/.kokoro/samples/python3.12/presubmit.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.12/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.13/common.cfg b/.kokoro/samples/python3.13/common.cfg deleted file mode 100644 index 549da16..0000000 --- a/.kokoro/samples/python3.13/common.cfg +++ /dev/null @@ -1,40 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Specify which tests to run -env_vars: { - key: "RUN_TESTS_SESSION" - value: "py-3.13" -} - -# Declare build specific Cloud project. -env_vars: { - key: "BUILD_SPECIFIC_GCLOUD_PROJECT" - value: "python-docs-samples-tests-313" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples.sh" -} - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" -} - -# Download secrets for samples -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "python-test-utils/.kokoro/trampoline_v2.sh" diff --git a/.kokoro/samples/python3.13/continuous.cfg b/.kokoro/samples/python3.13/continuous.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.13/continuous.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.13/periodic-head.cfg b/.kokoro/samples/python3.13/periodic-head.cfg deleted file mode 100644 index a36b71a..0000000 --- a/.kokoro/samples/python3.13/periodic-head.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples-against-head.sh" -} diff --git a/.kokoro/samples/python3.13/periodic.cfg b/.kokoro/samples/python3.13/periodic.cfg deleted file mode 100644 index 71cd1e5..0000000 --- a/.kokoro/samples/python3.13/periodic.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "False" -} diff --git a/.kokoro/samples/python3.13/presubmit.cfg b/.kokoro/samples/python3.13/presubmit.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.13/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.14/common.cfg b/.kokoro/samples/python3.14/common.cfg deleted file mode 100644 index 9f97b8c..0000000 --- a/.kokoro/samples/python3.14/common.cfg +++ /dev/null @@ -1,40 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Specify which tests to run -env_vars: { - key: "RUN_TESTS_SESSION" - value: "py-3.14" -} - -# Declare build specific Cloud project. -env_vars: { - key: "BUILD_SPECIFIC_GCLOUD_PROJECT" - value: "python-docs-samples-tests-314" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples.sh" -} - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" -} - -# Download secrets for samples -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "python-test-utils/.kokoro/trampoline_v2.sh" diff --git a/.kokoro/samples/python3.14/continuous.cfg b/.kokoro/samples/python3.14/continuous.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.14/continuous.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.14/periodic-head.cfg b/.kokoro/samples/python3.14/periodic-head.cfg deleted file mode 100644 index a36b71a..0000000 --- a/.kokoro/samples/python3.14/periodic-head.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples-against-head.sh" -} diff --git a/.kokoro/samples/python3.14/periodic.cfg b/.kokoro/samples/python3.14/periodic.cfg deleted file mode 100644 index 71cd1e5..0000000 --- a/.kokoro/samples/python3.14/periodic.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "False" -} diff --git a/.kokoro/samples/python3.14/presubmit.cfg b/.kokoro/samples/python3.14/presubmit.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.14/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.7/common.cfg b/.kokoro/samples/python3.7/common.cfg deleted file mode 100644 index 0a7f1bd..0000000 --- a/.kokoro/samples/python3.7/common.cfg +++ /dev/null @@ -1,40 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Specify which tests to run -env_vars: { - key: "RUN_TESTS_SESSION" - value: "py-3.7" -} - -# Declare build specific Cloud project. -env_vars: { - key: "BUILD_SPECIFIC_GCLOUD_PROJECT" - value: "python-docs-samples-tests-py37" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples.sh" -} - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" -} - -# Download secrets for samples -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "python-test-utils/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.7/continuous.cfg b/.kokoro/samples/python3.7/continuous.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.7/continuous.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.7/periodic-head.cfg b/.kokoro/samples/python3.7/periodic-head.cfg deleted file mode 100644 index a36b71a..0000000 --- a/.kokoro/samples/python3.7/periodic-head.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples-against-head.sh" -} diff --git a/.kokoro/samples/python3.7/periodic.cfg b/.kokoro/samples/python3.7/periodic.cfg deleted file mode 100644 index 71cd1e5..0000000 --- a/.kokoro/samples/python3.7/periodic.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "False" -} diff --git a/.kokoro/samples/python3.7/presubmit.cfg b/.kokoro/samples/python3.7/presubmit.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.7/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.8/common.cfg b/.kokoro/samples/python3.8/common.cfg deleted file mode 100644 index 07af128..0000000 --- a/.kokoro/samples/python3.8/common.cfg +++ /dev/null @@ -1,40 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Specify which tests to run -env_vars: { - key: "RUN_TESTS_SESSION" - value: "py-3.8" -} - -# Declare build specific Cloud project. -env_vars: { - key: "BUILD_SPECIFIC_GCLOUD_PROJECT" - value: "python-docs-samples-tests-py38" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples.sh" -} - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" -} - -# Download secrets for samples -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "python-test-utils/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.8/continuous.cfg b/.kokoro/samples/python3.8/continuous.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.8/continuous.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.8/periodic-head.cfg b/.kokoro/samples/python3.8/periodic-head.cfg deleted file mode 100644 index a36b71a..0000000 --- a/.kokoro/samples/python3.8/periodic-head.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples-against-head.sh" -} diff --git a/.kokoro/samples/python3.8/periodic.cfg b/.kokoro/samples/python3.8/periodic.cfg deleted file mode 100644 index 71cd1e5..0000000 --- a/.kokoro/samples/python3.8/periodic.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "False" -} diff --git a/.kokoro/samples/python3.8/presubmit.cfg b/.kokoro/samples/python3.8/presubmit.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.8/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.9/common.cfg b/.kokoro/samples/python3.9/common.cfg deleted file mode 100644 index 332ea1c..0000000 --- a/.kokoro/samples/python3.9/common.cfg +++ /dev/null @@ -1,40 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -# Build logs will be here -action { - define_artifacts { - regex: "**/*sponge_log.xml" - } -} - -# Specify which tests to run -env_vars: { - key: "RUN_TESTS_SESSION" - value: "py-3.9" -} - -# Declare build specific Cloud project. -env_vars: { - key: "BUILD_SPECIFIC_GCLOUD_PROJECT" - value: "python-docs-samples-tests-py39" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples.sh" -} - -# Configure the docker image for kokoro-trampoline. -env_vars: { - key: "TRAMPOLINE_IMAGE" - value: "gcr.io/cloud-devrel-kokoro-resources/python-samples-testing-docker" -} - -# Download secrets for samples -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/python-docs-samples" - -# Download trampoline resources. -gfile_resources: "/bigstore/cloud-devrel-kokoro-resources/trampoline" - -# Use the trampoline script to run in docker. -build_file: "python-test-utils/.kokoro/trampoline_v2.sh" \ No newline at end of file diff --git a/.kokoro/samples/python3.9/continuous.cfg b/.kokoro/samples/python3.9/continuous.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.9/continuous.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/samples/python3.9/periodic-head.cfg b/.kokoro/samples/python3.9/periodic-head.cfg deleted file mode 100644 index a36b71a..0000000 --- a/.kokoro/samples/python3.9/periodic-head.cfg +++ /dev/null @@ -1,11 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} - -env_vars: { - key: "TRAMPOLINE_BUILD_FILE" - value: "github/python-test-utils/.kokoro/test-samples-against-head.sh" -} diff --git a/.kokoro/samples/python3.9/periodic.cfg b/.kokoro/samples/python3.9/periodic.cfg deleted file mode 100644 index 71cd1e5..0000000 --- a/.kokoro/samples/python3.9/periodic.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "False" -} diff --git a/.kokoro/samples/python3.9/presubmit.cfg b/.kokoro/samples/python3.9/presubmit.cfg deleted file mode 100644 index a1c8d97..0000000 --- a/.kokoro/samples/python3.9/presubmit.cfg +++ /dev/null @@ -1,6 +0,0 @@ -# Format: //devtools/kokoro/config/proto/build.proto - -env_vars: { - key: "INSTALL_LIBRARY_FROM_SOURCE" - value: "True" -} \ No newline at end of file diff --git a/.kokoro/test-samples-against-head.sh b/.kokoro/test-samples-against-head.sh deleted file mode 100755 index e9d8bd7..0000000 --- a/.kokoro/test-samples-against-head.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# 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. - -# A customized test runner for samples. -# -# For periodic builds, you can specify this file for testing against head. - -# `-e` enables the script to automatically fail when a command fails -# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero -set -eo pipefail -# Enables `**` to include files nested inside sub-folders -shopt -s globstar - -exec .kokoro/test-samples-impl.sh diff --git a/.kokoro/test-samples-impl.sh b/.kokoro/test-samples-impl.sh deleted file mode 100755 index 53e365b..0000000 --- a/.kokoro/test-samples-impl.sh +++ /dev/null @@ -1,103 +0,0 @@ -#!/bin/bash -# 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. - - -# `-e` enables the script to automatically fail when a command fails -# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero -set -eo pipefail -# Enables `**` to include files nested inside sub-folders -shopt -s globstar - -# Exit early if samples don't exist -if ! find samples -name 'requirements.txt' | grep -q .; then - echo "No tests run. './samples/**/requirements.txt' not found" - exit 0 -fi - -# Disable buffering, so that the logs stream through. -export PYTHONUNBUFFERED=1 - -# Debug: show build environment -env | grep KOKORO - -# Install nox -# `virtualenv==20.26.6` is added for Python 3.7 compatibility -python3.9 -m pip install --upgrade --quiet nox virtualenv==20.26.6 - -# Use secrets acessor service account to get secrets -if [[ -f "${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" ]]; then - gcloud auth activate-service-account \ - --key-file="${KOKORO_GFILE_DIR}/secrets_viewer_service_account.json" \ - --project="cloud-devrel-kokoro-resources" -fi - -# This script will create 3 files: -# - testing/test-env.sh -# - testing/service-account.json -# - testing/client-secrets.json -./scripts/decrypt-secrets.sh - -source ./testing/test-env.sh -export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/testing/service-account.json - -# For cloud-run session, we activate the service account for gcloud sdk. -gcloud auth activate-service-account \ - --key-file "${GOOGLE_APPLICATION_CREDENTIALS}" - -export GOOGLE_CLIENT_SECRETS=$(pwd)/testing/client-secrets.json - -echo -e "\n******************** TESTING PROJECTS ********************" - -# Switch to 'fail at end' to allow all tests to complete before exiting. -set +e -# Use RTN to return a non-zero value if the test fails. -RTN=0 -ROOT=$(pwd) -# Find all requirements.txt in the samples directory (may break on whitespace). -for file in samples/**/requirements.txt; do - cd "$ROOT" - # Navigate to the project folder. - file=$(dirname "$file") - cd "$file" - - echo "------------------------------------------------------------" - echo "- testing $file" - echo "------------------------------------------------------------" - - # Use nox to execute the tests for the project. - python3.9 -m nox -s "$RUN_TESTS_SESSION" - EXIT=$? - - # If this is a periodic build, send the test log to the FlakyBot. - # See https://github.com/googleapis/repo-automation-bots/tree/main/packages/flakybot. - if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then - chmod +x $KOKORO_GFILE_DIR/linux_amd64/flakybot - $KOKORO_GFILE_DIR/linux_amd64/flakybot - fi - - if [[ $EXIT -ne 0 ]]; then - RTN=1 - echo -e "\n Testing failed: Nox returned a non-zero exit code. \n" - else - echo -e "\n Testing completed.\n" - fi - -done -cd "$ROOT" - -# Workaround for Kokoro permissions issue: delete secrets -rm testing/{test-env.sh,client-secrets.json,service-account.json} - -exit "$RTN" diff --git a/.kokoro/test-samples.sh b/.kokoro/test-samples.sh deleted file mode 100755 index 7933d82..0000000 --- a/.kokoro/test-samples.sh +++ /dev/null @@ -1,44 +0,0 @@ -#!/bin/bash -# 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. - -# The default test runner for samples. -# -# For periodic builds, we rewinds the repo to the latest release, and -# run test-samples-impl.sh. - -# `-e` enables the script to automatically fail when a command fails -# `-o pipefail` sets the exit code to the rightmost comment to exit with a non-zero -set -eo pipefail -# Enables `**` to include files nested inside sub-folders -shopt -s globstar - -# Run periodic samples tests at latest release -if [[ $KOKORO_BUILD_ARTIFACTS_SUBDIR = *"periodic"* ]]; then - # preserving the test runner implementation. - cp .kokoro/test-samples-impl.sh "${TMPDIR}/test-samples-impl.sh" - echo "--- IMPORTANT IMPORTANT IMPORTANT ---" - echo "Now we rewind the repo back to the latest release..." - LATEST_RELEASE=$(git describe --abbrev=0 --tags) - git checkout $LATEST_RELEASE - echo "The current head is: " - echo $(git rev-parse --verify HEAD) - echo "--- IMPORTANT IMPORTANT IMPORTANT ---" - # move back the test runner implementation if there's no file. - if [ ! -f .kokoro/test-samples-impl.sh ]; then - cp "${TMPDIR}/test-samples-impl.sh" .kokoro/test-samples-impl.sh - fi -fi - -exec .kokoro/test-samples-impl.sh diff --git a/.kokoro/trampoline.sh b/.kokoro/trampoline.sh deleted file mode 100755 index 48f7969..0000000 --- a/.kokoro/trampoline.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# 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. - -set -eo pipefail - -# Always run the cleanup script, regardless of the success of bouncing into -# the container. -function cleanup() { - chmod +x ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh - ${KOKORO_GFILE_DIR}/trampoline_cleanup.sh - echo "cleanup"; -} -trap cleanup EXIT - -$(dirname $0)/populate-secrets.sh # Secret Manager secrets. -python3 "${KOKORO_GFILE_DIR}/trampoline_v1.py" \ No newline at end of file diff --git a/.kokoro/trampoline_v2.sh b/.kokoro/trampoline_v2.sh deleted file mode 100755 index d03f92d..0000000 --- a/.kokoro/trampoline_v2.sh +++ /dev/null @@ -1,487 +0,0 @@ -#!/usr/bin/env bash -# 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. - -# trampoline_v2.sh -# -# This script does 3 things. -# -# 1. Prepare the Docker image for the test -# 2. Run the Docker with appropriate flags to run the test -# 3. Upload the newly built Docker image -# -# in a way that is somewhat compatible with trampoline_v1. -# -# To run this script, first download few files from gcs to /dev/shm. -# (/dev/shm is passed into the container as KOKORO_GFILE_DIR). -# -# gcloud storage cp gs://cloud-devrel-kokoro-resources/python-docs-samples/secrets_viewer_service_account.json /dev/shm -# gcloud storage cp gs://cloud-devrel-kokoro-resources/python-docs-samples/automl_secrets.txt /dev/shm -# -# Then run the script. -# .kokoro/trampoline_v2.sh -# -# These environment variables are required: -# TRAMPOLINE_IMAGE: The docker image to use. -# TRAMPOLINE_DOCKERFILE: The location of the Dockerfile. -# -# You can optionally change these environment variables: -# TRAMPOLINE_IMAGE_UPLOAD: -# (true|false): Whether to upload the Docker image after the -# successful builds. -# TRAMPOLINE_BUILD_FILE: The script to run in the docker container. -# TRAMPOLINE_WORKSPACE: The workspace path in the docker container. -# Defaults to /workspace. -# Potentially there are some repo specific envvars in .trampolinerc in -# the project root. - - -set -euo pipefail - -TRAMPOLINE_VERSION="2.0.5" - -if command -v tput >/dev/null && [[ -n "${TERM:-}" ]]; then - readonly IO_COLOR_RED="$(tput setaf 1)" - readonly IO_COLOR_GREEN="$(tput setaf 2)" - readonly IO_COLOR_YELLOW="$(tput setaf 3)" - readonly IO_COLOR_RESET="$(tput sgr0)" -else - readonly IO_COLOR_RED="" - readonly IO_COLOR_GREEN="" - readonly IO_COLOR_YELLOW="" - readonly IO_COLOR_RESET="" -fi - -function function_exists { - [ $(LC_ALL=C type -t $1)"" == "function" ] -} - -# Logs a message using the given color. The first argument must be one -# of the IO_COLOR_* variables defined above, such as -# "${IO_COLOR_YELLOW}". The remaining arguments will be logged in the -# given color. The log message will also have an RFC-3339 timestamp -# prepended (in UTC). You can disable the color output by setting -# TERM=vt100. -function log_impl() { - local color="$1" - shift - local timestamp="$(date -u "+%Y-%m-%dT%H:%M:%SZ")" - echo "================================================================" - echo "${color}${timestamp}:" "$@" "${IO_COLOR_RESET}" - echo "================================================================" -} - -# Logs the given message with normal coloring and a timestamp. -function log() { - log_impl "${IO_COLOR_RESET}" "$@" -} - -# Logs the given message in green with a timestamp. -function log_green() { - log_impl "${IO_COLOR_GREEN}" "$@" -} - -# Logs the given message in yellow with a timestamp. -function log_yellow() { - log_impl "${IO_COLOR_YELLOW}" "$@" -} - -# Logs the given message in red with a timestamp. -function log_red() { - log_impl "${IO_COLOR_RED}" "$@" -} - -readonly tmpdir=$(mktemp -d -t ci-XXXXXXXX) -readonly tmphome="${tmpdir}/h" -mkdir -p "${tmphome}" - -function cleanup() { - rm -rf "${tmpdir}" -} -trap cleanup EXIT - -RUNNING_IN_CI="${RUNNING_IN_CI:-false}" - -# The workspace in the container, defaults to /workspace. -TRAMPOLINE_WORKSPACE="${TRAMPOLINE_WORKSPACE:-/workspace}" - -pass_down_envvars=( - # TRAMPOLINE_V2 variables. - # Tells scripts whether they are running as part of CI or not. - "RUNNING_IN_CI" - # Indicates which CI system we're in. - "TRAMPOLINE_CI" - # Indicates the version of the script. - "TRAMPOLINE_VERSION" -) - -log_yellow "Building with Trampoline ${TRAMPOLINE_VERSION}" - -# Detect which CI systems we're in. If we're in any of the CI systems -# we support, `RUNNING_IN_CI` will be true and `TRAMPOLINE_CI` will be -# the name of the CI system. Both envvars will be passing down to the -# container for telling which CI system we're in. -if [[ -n "${KOKORO_BUILD_ID:-}" ]]; then - # descriptive env var for indicating it's on CI. - RUNNING_IN_CI="true" - TRAMPOLINE_CI="kokoro" - if [[ "${TRAMPOLINE_USE_LEGACY_SERVICE_ACCOUNT:-}" == "true" ]]; then - if [[ ! -f "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" ]]; then - log_red "${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json does not exist. Did you forget to mount cloud-devrel-kokoro-resources/trampoline? Aborting." - exit 1 - fi - # This service account will be activated later. - TRAMPOLINE_SERVICE_ACCOUNT="${KOKORO_GFILE_DIR}/kokoro-trampoline.service-account.json" - else - if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then - gcloud auth list - fi - log_yellow "Configuring Container Registry access" - gcloud auth configure-docker --quiet - fi - pass_down_envvars+=( - # KOKORO dynamic variables. - "KOKORO_BUILD_NUMBER" - "KOKORO_BUILD_ID" - "KOKORO_JOB_NAME" - "KOKORO_GIT_COMMIT" - "KOKORO_GITHUB_COMMIT" - "KOKORO_GITHUB_PULL_REQUEST_NUMBER" - "KOKORO_GITHUB_PULL_REQUEST_COMMIT" - # For FlakyBot - "KOKORO_GITHUB_COMMIT_URL" - "KOKORO_GITHUB_PULL_REQUEST_URL" - ) -elif [[ "${TRAVIS:-}" == "true" ]]; then - RUNNING_IN_CI="true" - TRAMPOLINE_CI="travis" - pass_down_envvars+=( - "TRAVIS_BRANCH" - "TRAVIS_BUILD_ID" - "TRAVIS_BUILD_NUMBER" - "TRAVIS_BUILD_WEB_URL" - "TRAVIS_COMMIT" - "TRAVIS_COMMIT_MESSAGE" - "TRAVIS_COMMIT_RANGE" - "TRAVIS_JOB_NAME" - "TRAVIS_JOB_NUMBER" - "TRAVIS_JOB_WEB_URL" - "TRAVIS_PULL_REQUEST" - "TRAVIS_PULL_REQUEST_BRANCH" - "TRAVIS_PULL_REQUEST_SHA" - "TRAVIS_PULL_REQUEST_SLUG" - "TRAVIS_REPO_SLUG" - "TRAVIS_SECURE_ENV_VARS" - "TRAVIS_TAG" - ) -elif [[ -n "${GITHUB_RUN_ID:-}" ]]; then - RUNNING_IN_CI="true" - TRAMPOLINE_CI="github-workflow" - pass_down_envvars+=( - "GITHUB_WORKFLOW" - "GITHUB_RUN_ID" - "GITHUB_RUN_NUMBER" - "GITHUB_ACTION" - "GITHUB_ACTIONS" - "GITHUB_ACTOR" - "GITHUB_REPOSITORY" - "GITHUB_EVENT_NAME" - "GITHUB_EVENT_PATH" - "GITHUB_SHA" - "GITHUB_REF" - "GITHUB_HEAD_REF" - "GITHUB_BASE_REF" - ) -elif [[ "${CIRCLECI:-}" == "true" ]]; then - RUNNING_IN_CI="true" - TRAMPOLINE_CI="circleci" - pass_down_envvars+=( - "CIRCLE_BRANCH" - "CIRCLE_BUILD_NUM" - "CIRCLE_BUILD_URL" - "CIRCLE_COMPARE_URL" - "CIRCLE_JOB" - "CIRCLE_NODE_INDEX" - "CIRCLE_NODE_TOTAL" - "CIRCLE_PREVIOUS_BUILD_NUM" - "CIRCLE_PROJECT_REPONAME" - "CIRCLE_PROJECT_USERNAME" - "CIRCLE_REPOSITORY_URL" - "CIRCLE_SHA1" - "CIRCLE_STAGE" - "CIRCLE_USERNAME" - "CIRCLE_WORKFLOW_ID" - "CIRCLE_WORKFLOW_JOB_ID" - "CIRCLE_WORKFLOW_UPSTREAM_JOB_IDS" - "CIRCLE_WORKFLOW_WORKSPACE_ID" - ) -fi - -# Configure the service account for pulling the docker image. -function repo_root() { - local dir="$1" - while [[ ! -d "${dir}/.git" ]]; do - dir="$(dirname "$dir")" - done - echo "${dir}" -} - -# Detect the project root. In CI builds, we assume the script is in -# the git tree and traverse from there, otherwise, traverse from `pwd` -# to find `.git` directory. -if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then - PROGRAM_PATH="$(realpath "$0")" - PROGRAM_DIR="$(dirname "${PROGRAM_PATH}")" - PROJECT_ROOT="$(repo_root "${PROGRAM_DIR}")" -else - PROJECT_ROOT="$(repo_root $(pwd))" -fi - -log_yellow "Changing to the project root: ${PROJECT_ROOT}." -cd "${PROJECT_ROOT}" - -# To support relative path for `TRAMPOLINE_SERVICE_ACCOUNT`, we need -# to use this environment variable in `PROJECT_ROOT`. -if [[ -n "${TRAMPOLINE_SERVICE_ACCOUNT:-}" ]]; then - - mkdir -p "${tmpdir}/gcloud" - gcloud_config_dir="${tmpdir}/gcloud" - - log_yellow "Using isolated gcloud config: ${gcloud_config_dir}." - export CLOUDSDK_CONFIG="${gcloud_config_dir}" - - log_yellow "Using ${TRAMPOLINE_SERVICE_ACCOUNT} for authentication." - gcloud auth activate-service-account \ - --key-file "${TRAMPOLINE_SERVICE_ACCOUNT}" - log_yellow "Configuring Container Registry access" - gcloud auth configure-docker --quiet -fi - -required_envvars=( - # The basic trampoline configurations. - "TRAMPOLINE_IMAGE" - "TRAMPOLINE_BUILD_FILE" -) - -if [[ -f "${PROJECT_ROOT}/.trampolinerc" ]]; then - source "${PROJECT_ROOT}/.trampolinerc" -fi - -log_yellow "Checking environment variables." -for e in "${required_envvars[@]}" -do - if [[ -z "${!e:-}" ]]; then - log "Missing ${e} env var. Aborting." - exit 1 - fi -done - -# We want to support legacy style TRAMPOLINE_BUILD_FILE used with V1 -# script: e.g. "github/repo-name/.kokoro/run_tests.sh" -TRAMPOLINE_BUILD_FILE="${TRAMPOLINE_BUILD_FILE#github/*/}" -log_yellow "Using TRAMPOLINE_BUILD_FILE: ${TRAMPOLINE_BUILD_FILE}" - -# ignore error on docker operations and test execution -set +e - -log_yellow "Preparing Docker image." -# We only download the docker image in CI builds. -if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then - # Download the docker image specified by `TRAMPOLINE_IMAGE` - - # We may want to add --max-concurrent-downloads flag. - - log_yellow "Start pulling the Docker image: ${TRAMPOLINE_IMAGE}." - if docker pull "${TRAMPOLINE_IMAGE}"; then - log_green "Finished pulling the Docker image: ${TRAMPOLINE_IMAGE}." - has_image="true" - else - log_red "Failed pulling the Docker image: ${TRAMPOLINE_IMAGE}." - has_image="false" - fi -else - # For local run, check if we have the image. - if docker images "${TRAMPOLINE_IMAGE}:latest" | grep "${TRAMPOLINE_IMAGE}"; then - has_image="true" - else - has_image="false" - fi -fi - - -# The default user for a Docker container has uid 0 (root). To avoid -# creating root-owned files in the build directory we tell docker to -# use the current user ID. -user_uid="$(id -u)" -user_gid="$(id -g)" -user_name="$(id -un)" - -# To allow docker in docker, we add the user to the docker group in -# the host os. -docker_gid=$(cut -d: -f3 < <(getent group docker)) - -update_cache="false" -if [[ "${TRAMPOLINE_DOCKERFILE:-none}" != "none" ]]; then - # Build the Docker image from the source. - context_dir=$(dirname "${TRAMPOLINE_DOCKERFILE}") - docker_build_flags=( - "-f" "${TRAMPOLINE_DOCKERFILE}" - "-t" "${TRAMPOLINE_IMAGE}" - "--build-arg" "UID=${user_uid}" - "--build-arg" "USERNAME=${user_name}" - ) - if [[ "${has_image}" == "true" ]]; then - docker_build_flags+=("--cache-from" "${TRAMPOLINE_IMAGE}") - fi - - log_yellow "Start building the docker image." - if [[ "${TRAMPOLINE_VERBOSE:-false}" == "true" ]]; then - echo "docker build" "${docker_build_flags[@]}" "${context_dir}" - fi - - # ON CI systems, we want to suppress docker build logs, only - # output the logs when it fails. - if [[ "${RUNNING_IN_CI:-}" == "true" ]]; then - if docker build "${docker_build_flags[@]}" "${context_dir}" \ - > "${tmpdir}/docker_build.log" 2>&1; then - if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then - cat "${tmpdir}/docker_build.log" - fi - - log_green "Finished building the docker image." - update_cache="true" - else - log_red "Failed to build the Docker image, aborting." - log_yellow "Dumping the build logs:" - cat "${tmpdir}/docker_build.log" - exit 1 - fi - else - if docker build "${docker_build_flags[@]}" "${context_dir}"; then - log_green "Finished building the docker image." - update_cache="true" - else - log_red "Failed to build the Docker image, aborting." - exit 1 - fi - fi -else - if [[ "${has_image}" != "true" ]]; then - log_red "We do not have ${TRAMPOLINE_IMAGE} locally, aborting." - exit 1 - fi -fi - -# We use an array for the flags so they are easier to document. -docker_flags=( - # Remove the container after it exists. - "--rm" - - # Use the host network. - "--network=host" - - # Run in priviledged mode. We are not using docker for sandboxing or - # isolation, just for packaging our dev tools. - "--privileged" - - # Run the docker script with the user id. Because the docker image gets to - # write in ${PWD} you typically want this to be your user id. - # To allow docker in docker, we need to use docker gid on the host. - "--user" "${user_uid}:${docker_gid}" - - # Pass down the USER. - "--env" "USER=${user_name}" - - # Mount the project directory inside the Docker container. - "--volume" "${PROJECT_ROOT}:${TRAMPOLINE_WORKSPACE}" - "--workdir" "${TRAMPOLINE_WORKSPACE}" - "--env" "PROJECT_ROOT=${TRAMPOLINE_WORKSPACE}" - - # Mount the temporary home directory. - "--volume" "${tmphome}:/h" - "--env" "HOME=/h" - - # Allow docker in docker. - "--volume" "/var/run/docker.sock:/var/run/docker.sock" - - # Mount the /tmp so that docker in docker can mount the files - # there correctly. - "--volume" "/tmp:/tmp" - # Pass down the KOKORO_GFILE_DIR and KOKORO_KEYSTORE_DIR - # TODO(tmatsuo): This part is not portable. - "--env" "TRAMPOLINE_SECRET_DIR=/secrets" - "--volume" "${KOKORO_GFILE_DIR:-/dev/shm}:/secrets/gfile" - "--env" "KOKORO_GFILE_DIR=/secrets/gfile" - "--volume" "${KOKORO_KEYSTORE_DIR:-/dev/shm}:/secrets/keystore" - "--env" "KOKORO_KEYSTORE_DIR=/secrets/keystore" -) - -# Add an option for nicer output if the build gets a tty. -if [[ -t 0 ]]; then - docker_flags+=("-it") -fi - -# Passing down env vars -for e in "${pass_down_envvars[@]}" -do - if [[ -n "${!e:-}" ]]; then - docker_flags+=("--env" "${e}=${!e}") - fi -done - -# If arguments are given, all arguments will become the commands run -# in the container, otherwise run TRAMPOLINE_BUILD_FILE. -if [[ $# -ge 1 ]]; then - log_yellow "Running the given commands '" "${@:1}" "' in the container." - readonly commands=("${@:1}") - if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then - echo docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" "${commands[@]}" - fi - docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" "${commands[@]}" -else - log_yellow "Running the tests in a Docker container." - docker_flags+=("--entrypoint=${TRAMPOLINE_BUILD_FILE}") - if [[ "${TRAMPOLINE_VERBOSE:-}" == "true" ]]; then - echo docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" - fi - docker run "${docker_flags[@]}" "${TRAMPOLINE_IMAGE}" -fi - - -test_retval=$? - -if [[ ${test_retval} -eq 0 ]]; then - log_green "Build finished with ${test_retval}" -else - log_red "Build finished with ${test_retval}" -fi - -# Only upload it when the test passes. -if [[ "${update_cache}" == "true" ]] && \ - [[ $test_retval == 0 ]] && \ - [[ "${TRAMPOLINE_IMAGE_UPLOAD:-false}" == "true" ]]; then - log_yellow "Uploading the Docker image." - if docker push "${TRAMPOLINE_IMAGE}"; then - log_green "Finished uploading the Docker image." - else - log_red "Failed uploading the Docker image." - fi - # Call trampoline_after_upload_hook if it's defined. - if function_exists trampoline_after_upload_hook; then - trampoline_after_upload_hook - fi - -fi - -exit "${test_retval}" diff --git a/.librarian/state.yaml b/.librarian/state.yaml deleted file mode 100644 index b45c914..0000000 --- a/.librarian/state.yaml +++ /dev/null @@ -1,10 +0,0 @@ -image: us-central1-docker.pkg.dev/cloud-sdk-librarian-prod/images-prod/python-librarian-generator@sha256:c8612d3fffb3f6a32353b2d1abd16b61e87811866f7ec9d65b59b02eb452a620 -libraries: - - id: google-cloud-testutils - version: 1.7.0 - apis: [] - source_roots: - - . - preserve_regex: [] - remove_regex: [] - tag_format: v{version} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml deleted file mode 100644 index 1d74695..0000000 --- a/.pre-commit-config.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. -# -# See https://pre-commit.com for more information -# See https://pre-commit.com/hooks.html for more hooks -repos: -- repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.0.1 - hooks: - - id: trailing-whitespace - - id: end-of-file-fixer - - id: check-yaml -- repo: https://github.com/psf/black - rev: 23.7.0 - hooks: - - id: black -- repo: https://github.com/pycqa/flake8 - rev: 6.1.0 - hooks: - - id: flake8 diff --git a/.trampolinerc b/.trampolinerc deleted file mode 100644 index 0080152..0000000 --- a/.trampolinerc +++ /dev/null @@ -1,61 +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. - -# Add required env vars here. -required_envvars+=( -) - -# Add env vars which are passed down into the container here. -pass_down_envvars+=( - "NOX_SESSION" - ############### - # Docs builds - ############### - "STAGING_BUCKET" - "V2_STAGING_BUCKET" - ################## - # Samples builds - ################## - "INSTALL_LIBRARY_FROM_SOURCE" - "RUN_TESTS_SESSION" - "BUILD_SPECIFIC_GCLOUD_PROJECT" - # Target directories. - "RUN_TESTS_DIRS" - # The nox session to run. - "RUN_TESTS_SESSION" -) - -# Prevent unintentional override on the default image. -if [[ "${TRAMPOLINE_IMAGE_UPLOAD:-false}" == "true" ]] && \ - [[ -z "${TRAMPOLINE_IMAGE:-}" ]]; then - echo "Please set TRAMPOLINE_IMAGE if you want to upload the Docker image." - exit 1 -fi - -# Define the default value if it makes sense. -if [[ -z "${TRAMPOLINE_IMAGE_UPLOAD:-}" ]]; then - TRAMPOLINE_IMAGE_UPLOAD="" -fi - -if [[ -z "${TRAMPOLINE_IMAGE:-}" ]]; then - TRAMPOLINE_IMAGE="" -fi - -if [[ -z "${TRAMPOLINE_DOCKERFILE:-}" ]]; then - TRAMPOLINE_DOCKERFILE="" -fi - -if [[ -z "${TRAMPOLINE_BUILD_FILE:-}" ]]; then - TRAMPOLINE_BUILD_FILE="" -fi diff --git a/CHANGELOG.md b/CHANGELOG.md deleted file mode 100644 index 75a9655..0000000 --- a/CHANGELOG.md +++ /dev/null @@ -1,131 +0,0 @@ -# Changelog - -[PyPI History][1] - -[1]: https://pypi.org/project/google-cloud-testutils/#history - -## [1.7.0](https://github.com/googleapis/python-test-utils/compare/v1.6.4...v1.7.0) (2025-10-29) - - -### Features - -* Add Python 3.14 support ([#284](https://github.com/googleapis/python-test-utils/issues/284)) ([3cb8491](https://github.com/googleapis/python-test-utils/commit/3cb8491d67d65d2262aa1b65091ea9b615b583af)) - -## [1.6.4](https://github.com/googleapis/python-test-utils/compare/v1.6.3...v1.6.4) (2025-05-19) - - -### Miscellaneous Chores - -* Force release for testing ([#279](https://github.com/googleapis/python-test-utils/issues/279)) ([7333a49](https://github.com/googleapis/python-test-utils/commit/7333a4904bab456e2274fb1dd2610c868dd331ed)) - -## [1.6.3](https://github.com/googleapis/python-test-utils/compare/v1.6.2...v1.6.3) (2025-05-05) - - -### Miscellaneous Chores - -* Force release for testing ([#277](https://github.com/googleapis/python-test-utils/issues/277)) ([05ad7f1](https://github.com/googleapis/python-test-utils/commit/05ad7f162ff5dd0d03196e9d96eaa88112ddb1b6)) - -## [1.6.2](https://github.com/googleapis/python-test-utils/compare/v1.6.1...v1.6.2) (2025-04-28) - - -### Miscellaneous Chores - -* Force rebuild ([#275](https://github.com/googleapis/python-test-utils/issues/275)) ([40fb90e](https://github.com/googleapis/python-test-utils/commit/40fb90e61f49e8e96b73cfe2c80133a5768b98c0)) - -## [1.6.1](https://github.com/googleapis/python-test-utils/compare/v1.6.0...v1.6.1) (2025-04-22) - - -### Bug Fixes - -* Remove setup.cfg configuration for creating universal wheels ([#272](https://github.com/googleapis/python-test-utils/issues/272)) ([32a23d2](https://github.com/googleapis/python-test-utils/commit/32a23d2e434e133d2b16e4afed6b80890b544968)) - -## [1.6.0](https://github.com/googleapis/python-test-utils/compare/v1.5.0...v1.6.0) (2025-01-30) - - -### Features - -* Add support for `test_utils.__version__` ([#244](https://github.com/googleapis/python-test-utils/issues/244)) ([9655669](https://github.com/googleapis/python-test-utils/commit/9655669de131cd7e0d67b3d6377f49063b5c2acb)) - -## [1.5.0](https://github.com/googleapis/python-test-utils/compare/v1.4.0...v1.5.0) (2024-11-12) - - -### Features - -* Add support for Python 3.13 ([#219](https://github.com/googleapis/python-test-utils/issues/219)) ([37b1ff1](https://github.com/googleapis/python-test-utils/commit/37b1ff1c3473922a57b1858955e0efe94cca1db1)) - -## [1.4.0](https://github.com/googleapis/python-test-utils/compare/v1.3.3...v1.4.0) (2023-11-29) - - -### Features - -* Add support for Python 3.12 ([474961a](https://github.com/googleapis/python-test-utils/commit/474961aa62ec598f8aa658b92032f1843a507116)) - -## [1.3.3](https://github.com/googleapis/python-test-utils/compare/v1.3.2...v1.3.3) (2022-07-10) - - -### Bug Fixes - -* require python 3.7+ ([#107](https://github.com/googleapis/python-test-utils/issues/107)) ([eb41a45](https://github.com/googleapis/python-test-utils/commit/eb41a4549c218f3bed3f57acc78872ae0d0bf2bf)) - -## [1.3.2](https://github.com/googleapis/python-test-utils/compare/v1.3.1...v1.3.2) (2022-06-06) - - -### Documentation - -* fix changelog header to consistent size ([#100](https://github.com/googleapis/python-test-utils/issues/100)) ([a446cdc](https://github.com/googleapis/python-test-utils/commit/a446cdcb4b9e32c7066da82e2e6cae4a6210d85a)) - -## [1.3.1](https://www.github.com/googleapis/python-test-utils/compare/v1.3.0...v1.3.1) (2021-12-07) - - -### Bug Fixes - -* ensure that 'test_utils/py.typed' is packaged ([#76](https://www.github.com/googleapis/python-test-utils/issues/76)) ([4beb565](https://www.github.com/googleapis/python-test-utils/commit/4beb565a4063cb462dc44e469fb91212607016f3)) - -## [1.3.0](https://www.github.com/googleapis/python-test-utils/compare/v1.2.0...v1.3.0) (2021-11-16) - - -### Features - -* add 'py.typed' declaration ([#73](https://www.github.com/googleapis/python-test-utils/issues/73)) ([f8f5f0a](https://www.github.com/googleapis/python-test-utils/commit/f8f5f0a194b2420b2fee1cf88ac50220d3ba1538)) - -## [1.2.0](https://www.github.com/googleapis/python-test-utils/compare/v1.1.0...v1.2.0) (2021-10-18) - - -### Features - -* add support for python 3.10 ([#68](https://www.github.com/googleapis/python-test-utils/issues/68)) ([d93b6a1](https://www.github.com/googleapis/python-test-utils/commit/d93b6a11e3bfade2b29ab90ed3bc2c384beb01cd)) - -## [1.1.0](https://www.github.com/googleapis/python-test-utils/compare/v1.0.0...v1.1.0) (2021-08-30) - - -### Features - -* add 'orchestrate' module ([#54](https://www.github.com/googleapis/python-test-utils/issues/54)) ([ae3da1a](https://www.github.com/googleapis/python-test-utils/commit/ae3da1ab4e7cbf268d6dce60cb467ca7ed6c2c89)) - -## [1.0.0](https://www.github.com/googleapis/python-test-utils/compare/v0.3.0...v1.0.0) (2021-08-02) - - -### ⚠ BREAKING CHANGES - -* drop support for Python 2.7 ([#43](https://www.github.com/googleapis/python-test-utils/issues/43)) ([f5e9c65](https://www.github.com/googleapis/python-test-utils/commit/f5e9c6535481e1ed70fa5e356668e5b0695481e0)) - -## [0.3.0](https://www.github.com/googleapis/python-test-utils/compare/v0.2.1...v0.3.0) (2021-07-07) - - -### Features - -* add Prefixer class to generate and parse resource names ([#39](https://www.github.com/googleapis/python-test-utils/issues/39)) ([865480b](https://www.github.com/googleapis/python-test-utils/commit/865480b5f62bf0db3b14000019a276aea102299d)) - -## [0.2.1](https://www.github.com/googleapis/python-test-utils/compare/v0.2.0...v0.2.1) (2021-06-29) - - -### Bug Fixes - -* use 'six.wraps' vs. 'functools.wraps' ([#37](https://www.github.com/googleapis/python-test-utils/issues/37)) ([701c3a4](https://www.github.com/googleapis/python-test-utils/commit/701c3a41fcf0a63c2b8b689493fa2ae21304511b)) - -## [0.2.0](https://www.github.com/googleapis/python-test-utils/compare/v0.1.0...v0.2.0) (2021-02-22) - - -### Features - -* add lower bound checker ([#8](https://www.github.com/googleapis/python-test-utils/issues/8)) ([5ebac9f](https://www.github.com/googleapis/python-test-utils/commit/5ebac9fb0ad005f8ea947c14dfca6de3c0d2cac9)) diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 039f436..0000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,95 +0,0 @@ - -# Code of Conduct - -## Our Pledge - -In the interest of fostering an open and welcoming environment, we as -contributors and maintainers pledge to making participation in our project and -our community a harassment-free experience for everyone, regardless of age, body -size, disability, ethnicity, gender identity and expression, level of -experience, education, socio-economic status, nationality, personal appearance, -race, religion, or sexual identity and orientation. - -## Our Standards - -Examples of behavior that contributes to creating a positive environment -include: - -* Using welcoming and inclusive language -* Being respectful of differing viewpoints and experiences -* Gracefully accepting constructive criticism -* Focusing on what is best for the community -* Showing empathy towards other community members - -Examples of unacceptable behavior by participants include: - -* The use of sexualized language or imagery and unwelcome sexual attention or - advances -* Trolling, insulting/derogatory comments, and personal or political attacks -* Public or private harassment -* Publishing others' private information, such as a physical or electronic - address, without explicit permission -* Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Our Responsibilities - -Project maintainers are responsible for clarifying the standards of acceptable -behavior and are expected to take appropriate and fair corrective action in -response to any instances of unacceptable behavior. - -Project maintainers have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, or to ban temporarily or permanently any -contributor for other behaviors that they deem inappropriate, threatening, -offensive, or harmful. - -## Scope - -This Code of Conduct applies both within project spaces and in public spaces -when an individual is representing the project or its community. Examples of -representing a project or community include using an official project e-mail -address, posting via an official social media account, or acting as an appointed -representative at an online or offline event. Representation of a project may be -further defined and clarified by project maintainers. - -This Code of Conduct also applies outside the project spaces when the Project -Steward has a reasonable belief that an individual's behavior may have a -negative impact on the project or its community. - -## Conflict Resolution - -We do not believe that all conflict is bad; healthy debate and disagreement -often yield positive results. However, it is never okay to be disrespectful or -to engage in behavior that violates the project’s code of conduct. - -If you see someone violating the code of conduct, you are encouraged to address -the behavior directly with those involved. Many issues can be resolved quickly -and easily, and this gives people more control over the outcome of their -dispute. If you are unable to resolve the matter for any reason, or if the -behavior is threatening or harassing, report it. We are dedicated to providing -an environment where participants feel welcome and safe. - - -Reports should be directed to *googleapis-stewards@google.com*, the -Project Steward(s) for *Google Cloud Client Libraries*. It is the Project Steward’s duty to -receive and address reported violations of the code of conduct. They will then -work with a committee consisting of representatives from the Open Source -Programs Office and the Google Open Source Strategy team. If for any reason you -are uncomfortable reaching out to the Project Steward, please email -opensource@google.com. - -We will investigate every complaint, but you may not receive a direct response. -We will use our discretion in determining when and how to follow up on reported -incidents, which may range from not taking action to permanent expulsion from -the project and project-sponsored spaces. We will notify the accused of the -report and provide them an opportunity to discuss it before any action is taken. -The identity of the reporter will be omitted from the details of the report -supplied to the accused. In potentially harmful situations, such as ongoing -harassment or threats to anyone's safety, we may take action without notice. - -## Attribution - -This Code of Conduct is adapted from the Contributor Covenant, version 1.4, -available at -https://www.contributor-covenant.org/version/1/4/code-of-conduct.html \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index b48b406..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 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. - -# Generated by synthtool. DO NOT EDIT! -include README.rst LICENSE -recursive-include test_utils *.json *.proto py.typed -recursive-include tests * -global-exclude *.py[co] -global-exclude __pycache__ - -# Exclude scripts for samples readmegen -prune scripts/readme-gen diff --git a/README.rst b/README.rst index f2b25dd..0f90e5d 100644 --- a/README.rst +++ b/README.rst @@ -1,3 +1,8 @@ +:**NOTE**: **This github repository is archived. The repository contents and history have moved to** `google-cloud-python`_. + +.. _google-cloud-python: https://github.com/googleapis/google-cloud-python/tree/main/packages/google-cloud-testutils + + ################# Python Test Utils ################# diff --git a/mypy.ini b/mypy.ini deleted file mode 100644 index 8efb6f7..0000000 --- a/mypy.ini +++ /dev/null @@ -1,3 +0,0 @@ -[mypy] -python_version = 3.9 -exclude = tests/unit/resources/ diff --git a/noxfile.py b/noxfile.py deleted file mode 100644 index d23707d..0000000 --- a/noxfile.py +++ /dev/null @@ -1,148 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2020 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 __future__ import absolute_import -import os -import pathlib -import shutil - -import nox - -# 'update_lower_bounds' is excluded -nox.options.sessions = [ - "lint", - "blacken", - "lint_setup_py", - "mypy", - "unit", - "check_lower_bounds", -] - - -# Error if a python version is missing -nox.options.error_on_missing_interpreters = True - -DEFAULT_PYTHON_VERSION = "3.14" -BLACK_VERSION = "black==23.7.0" -BLACK_PATHS = ["test_utils", "setup.py"] -CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() - - -@nox.session(python=DEFAULT_PYTHON_VERSION) -def lint(session): - """Run linters. - - Returns a failure if the linters find linting errors or sufficiently - serious code quality issues. - """ - session.install("flake8", BLACK_VERSION) - session.run( - "black", - "--check", - *BLACK_PATHS, - ) - session.run("flake8", *BLACK_PATHS) - - -@nox.session(python=DEFAULT_PYTHON_VERSION) -def blacken(session): - """Run black. - - Format code to uniform standard. - """ - session.install(BLACK_VERSION) - session.run( - "black", - *BLACK_PATHS, - ) - - -@nox.session(python=DEFAULT_PYTHON_VERSION) -def lint_setup_py(session): - """Verify that setup.py is valid (including RST check).""" - session.install("docutils", "pygments", "setuptools") - session.run("python", "setup.py", "check", "--restructuredtext", "--strict") - - -@nox.session(python=DEFAULT_PYTHON_VERSION) -def mypy(session): - """Verify type hints are mypy compatible.""" - session.install("-e", ".") - session.install( - "mypy", - "types-mock", - "types-setuptools", - ) - session.run("mypy", "test_utils/", "tests/") - - -@nox.session(python=["3.7", "3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]) -def unit(session): - constraints_path = str( - CURRENT_DIRECTORY / "testing" / f"constraints-{session.python}.txt" - ) - - # Install two fake packages for the lower-bound-checker tests - session.install( - "-e", "tests/unit/resources/good_package", "tests/unit/resources/bad_package" - ) - - session.install("pytest", "pytest-cov") - session.install("-e", ".", "-c", constraints_path) - - # Run py.test against the unit tests. - session.run( - "py.test", - "--quiet", - f"--junitxml=unit_{session.python}_sponge_log.xml", - "--cov=test_utils", - "--cov=tests/unit", - "--cov-append", - "--cov-config=.coveragerc", - "--cov-report=", - "--cov-fail-under=0", - os.path.join("tests", "unit"), - *session.posargs, - ) - - -@nox.session(python=DEFAULT_PYTHON_VERSION) -def check_lower_bounds(session): - """Check lower bounds in setup.py are reflected in constraints file""" - session.install(".") - session.run( - "lower-bound-checker", - "check", - "--package-name", - "google-cloud-testutils", - "--constraints-file", - "testing/constraints-3.7.txt", - ) - - -@nox.session(python=DEFAULT_PYTHON_VERSION) -def update_lower_bounds(session): - """Update lower bounds in constraints.txt to match setup.py""" - session.install(".") - session.run( - "lower-bound-checker", - "update", - "--package-name", - "google-cloud-testutils", - "--constraints-file", - "testing/constraints-3.7.txt", - ) diff --git a/pytest.ini b/pytest.ini deleted file mode 100644 index 3e84902..0000000 --- a/pytest.ini +++ /dev/null @@ -1,4 +0,0 @@ -[pytest] -filterwarnings = - # treat all warnings as errors - error diff --git a/renovate.json b/renovate.json deleted file mode 100644 index dbdcb7b..0000000 --- a/renovate.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "extends": [ - "config:base", - ":preserveSemverRanges", - ":disableDependencyDashboard" - ], - "ignorePaths": [ - ".pre-commit-config.yaml", - "tests/unit/resources", - ".kokoro/requirements.txt" - ], - "pip_requirements": { - "fileMatch": [ - "requirements-test.txt", - "samples/[\\S/]*constraints.txt", - "samples/[\\S/]*constraints-test.txt" - ] - } -} diff --git a/scripts/decrypt-secrets.sh b/scripts/decrypt-secrets.sh deleted file mode 100755 index 120b0dd..0000000 --- a/scripts/decrypt-secrets.sh +++ /dev/null @@ -1,46 +0,0 @@ -#!/bin/bash - -# Copyright 2024 Google LLC 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. - -DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -ROOT=$( dirname "$DIR" ) - -# Work from the project root. -cd $ROOT - -# Prevent it from overriding files. -# We recommend that sample authors use their own service account files and cloud project. -# In that case, they are supposed to prepare these files by themselves. -if [[ -f "testing/test-env.sh" ]] || \ - [[ -f "testing/service-account.json" ]] || \ - [[ -f "testing/client-secrets.json" ]]; then - echo "One or more target files exist, aborting." - exit 1 -fi - -# Use SECRET_MANAGER_PROJECT if set, fallback to cloud-devrel-kokoro-resources. -PROJECT_ID="${SECRET_MANAGER_PROJECT:-cloud-devrel-kokoro-resources}" - -gcloud secrets versions access latest --secret="python-docs-samples-test-env" \ - --project="${PROJECT_ID}" \ - > testing/test-env.sh -gcloud secrets versions access latest \ - --secret="python-docs-samples-service-account" \ - --project="${PROJECT_ID}" \ - > testing/service-account.json -gcloud secrets versions access latest \ - --secret="python-docs-samples-client-secrets" \ - --project="${PROJECT_ID}" \ - > testing/client-secrets.json diff --git a/scripts/readme-gen/readme_gen.py b/scripts/readme-gen/readme_gen.py deleted file mode 100644 index 8f5e248..0000000 --- a/scripts/readme-gen/readme_gen.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/usr/bin/env python - -# 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. - -"""Generates READMEs using configuration defined in yaml.""" - -import argparse -import io -import os -import subprocess - -import jinja2 -import yaml - - -jinja_env = jinja2.Environment( - trim_blocks=True, - loader=jinja2.FileSystemLoader( - os.path.abspath(os.path.join(os.path.dirname(__file__), "templates")) - ), - autoescape=True, -) - -README_TMPL = jinja_env.get_template("README.tmpl.rst") - - -def get_help(file): - return subprocess.check_output(["python", file, "--help"]).decode() - - -def main(): - parser = argparse.ArgumentParser() - parser.add_argument("source") - parser.add_argument("--destination", default="README.rst") - - args = parser.parse_args() - - source = os.path.abspath(args.source) - root = os.path.dirname(source) - destination = os.path.join(root, args.destination) - - jinja_env.globals["get_help"] = get_help - - with io.open(source, "r") as f: - config = yaml.load(f) - - # This allows get_help to execute in the right directory. - os.chdir(root) - - output = README_TMPL.render(config) - - with io.open(destination, "w") as f: - f.write(output) - - -if __name__ == "__main__": - main() diff --git a/scripts/readme-gen/templates/README.tmpl.rst b/scripts/readme-gen/templates/README.tmpl.rst deleted file mode 100644 index 4fd2397..0000000 --- a/scripts/readme-gen/templates/README.tmpl.rst +++ /dev/null @@ -1,87 +0,0 @@ -{# The following line is a lie. BUT! Once jinja2 is done with it, it will - become truth! #} -.. This file is automatically generated. Do not edit this file directly. - -{{product.name}} Python Samples -=============================================================================== - -.. image:: https://gstatic.com/cloudssh/images/open-btn.png - :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor={{folder}}/README.rst - - -This directory contains samples for {{product.name}}. {{product.description}} - -{{description}} - -.. _{{product.name}}: {{product.url}} - -{% if required_api_url %} -To run the sample, you need to enable the API at: {{required_api_url}} -{% endif %} - -{% if required_role %} -To run the sample, you need to have `{{required_role}}` role. -{% endif %} - -{{other_required_steps}} - -{% if setup %} -Setup -------------------------------------------------------------------------------- - -{% for section in setup %} - -{% include section + '.tmpl.rst' %} - -{% endfor %} -{% endif %} - -{% if samples %} -Samples -------------------------------------------------------------------------------- - -{% for sample in samples %} -{{sample.name}} -+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ - -{% if not sample.hide_cloudshell_button %} -.. image:: https://gstatic.com/cloudssh/images/open-btn.png - :target: https://console.cloud.google.com/cloudshell/open?git_repo=https://github.com/GoogleCloudPlatform/python-docs-samples&page=editor&open_in_editor={{folder}}/{{sample.file}},{{folder}}/README.rst -{% endif %} - - -{{sample.description}} - -To run this sample: - -.. code-block:: bash - - $ python {{sample.file}} -{% if sample.show_help %} - - {{get_help(sample.file)|indent}} -{% endif %} - - -{% endfor %} -{% endif %} - -{% if cloud_client_library %} - -The client library -------------------------------------------------------------------------------- - -This sample uses the `Google Cloud Client Library for Python`_. -You can read the documentation for more details on API usage and use GitHub -to `browse the source`_ and `report issues`_. - -.. _Google Cloud Client Library for Python: - https://googlecloudplatform.github.io/google-cloud-python/ -.. _browse the source: - https://github.com/GoogleCloudPlatform/google-cloud-python -.. _report issues: - https://github.com/GoogleCloudPlatform/google-cloud-python/issues - -{% endif %} - -.. _Google Cloud SDK: https://cloud.google.com/sdk/ \ No newline at end of file diff --git a/scripts/readme-gen/templates/auth.tmpl.rst b/scripts/readme-gen/templates/auth.tmpl.rst deleted file mode 100644 index 1446b94..0000000 --- a/scripts/readme-gen/templates/auth.tmpl.rst +++ /dev/null @@ -1,9 +0,0 @@ -Authentication -++++++++++++++ - -This sample requires you to have authentication setup. Refer to the -`Authentication Getting Started Guide`_ for instructions on setting up -credentials for applications. - -.. _Authentication Getting Started Guide: - https://cloud.google.com/docs/authentication/getting-started diff --git a/scripts/readme-gen/templates/auth_api_key.tmpl.rst b/scripts/readme-gen/templates/auth_api_key.tmpl.rst deleted file mode 100644 index 11957ce..0000000 --- a/scripts/readme-gen/templates/auth_api_key.tmpl.rst +++ /dev/null @@ -1,14 +0,0 @@ -Authentication -++++++++++++++ - -Authentication for this service is done via an `API Key`_. To obtain an API -Key: - -1. Open the `Cloud Platform Console`_ -2. Make sure that billing is enabled for your project. -3. From the **Credentials** page, create a new **API Key** or use an existing - one for your project. - -.. _API Key: - https://developers.google.com/api-client-library/python/guide/aaa_apikeys -.. _Cloud Console: https://console.cloud.google.com/project?_ diff --git a/scripts/readme-gen/templates/install_deps.tmpl.rst b/scripts/readme-gen/templates/install_deps.tmpl.rst deleted file mode 100644 index 6f069c6..0000000 --- a/scripts/readme-gen/templates/install_deps.tmpl.rst +++ /dev/null @@ -1,29 +0,0 @@ -Install Dependencies -++++++++++++++++++++ - -#. Clone python-docs-samples and change directory to the sample directory you want to use. - - .. code-block:: bash - - $ git clone https://github.com/GoogleCloudPlatform/python-docs-samples.git - -#. Install `pip`_ and `virtualenv`_ if you do not already have them. You may want to refer to the `Python Development Environment Setup Guide`_ for Google Cloud Platform for instructions. - - .. _Python Development Environment Setup Guide: - https://cloud.google.com/python/setup - -#. Create a virtualenv. Samples are compatible with Python 3.7+. - - .. code-block:: bash - - $ virtualenv env - $ source env/bin/activate - -#. Install the dependencies needed to run the samples. - - .. code-block:: bash - - $ pip install -r requirements.txt - -.. _pip: https://pip.pypa.io/ -.. _virtualenv: https://virtualenv.pypa.io/ diff --git a/scripts/readme-gen/templates/install_portaudio.tmpl.rst b/scripts/readme-gen/templates/install_portaudio.tmpl.rst deleted file mode 100644 index 5ea33d1..0000000 --- a/scripts/readme-gen/templates/install_portaudio.tmpl.rst +++ /dev/null @@ -1,35 +0,0 @@ -Install PortAudio -+++++++++++++++++ - -Install `PortAudio`_. This is required by the `PyAudio`_ library to stream -audio from your computer's microphone. PyAudio depends on PortAudio for cross-platform compatibility, and is installed differently depending on the -platform. - -* For Mac OS X, you can use `Homebrew`_:: - - brew install portaudio - - **Note**: if you encounter an error when running `pip install` that indicates - it can't find `portaudio.h`, try running `pip install` with the following - flags:: - - pip install --global-option='build_ext' \ - --global-option='-I/usr/local/include' \ - --global-option='-L/usr/local/lib' \ - pyaudio - -* For Debian / Ubuntu Linux:: - - apt-get install portaudio19-dev python-all-dev - -* Windows may work without having to install PortAudio explicitly (it will get - installed with PyAudio). - -For more details, see the `PyAudio installation`_ page. - - -.. _PyAudio: https://people.csail.mit.edu/hubert/pyaudio/ -.. _PortAudio: http://www.portaudio.com/ -.. _PyAudio installation: - https://people.csail.mit.edu/hubert/pyaudio/#downloads -.. _Homebrew: http://brew.sh diff --git a/setup.py b/setup.py deleted file mode 100644 index 9b7b0c9..0000000 --- a/setup.py +++ /dev/null @@ -1,81 +0,0 @@ -# Copyright 2017 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. - -import io -import os -import re -import setuptools # type: ignore - -version = None - -PACKAGE_ROOT = os.path.abspath(os.path.dirname(__file__)) - -with open(os.path.join(PACKAGE_ROOT, "test_utils/version.py")) as fp: - version_candidates = re.findall(r"(?<=\")\d+.\d+.\d+(?=\")", fp.read()) - assert len(version_candidates) == 1 - version = version_candidates[0] - -package_root = os.path.abspath(os.path.dirname(__file__)) - -readme_filename = os.path.join(package_root, "README.rst") -with io.open(readme_filename, encoding="utf-8") as readme_file: - readme = readme_file.read() - -scripts = ( - ["lower-bound-checker=test_utils.lower_bound_checker.lower_bound_checker:main"], -) - -packages = [ - package - for package in setuptools.find_namespace_packages() - if package.startswith("test_utils") -] - -setuptools.setup( - name="google-cloud-testutils", - version=version, - long_description=readme, - author="Google LLC", - author_email="googleapis-packages@google.com", - license="Apache 2.0", - url="https://github.com/googleapis/python-test-utils", - packages=packages, - entry_points={"console_scripts": scripts}, - platforms="Posix; MacOS X; Windows", - include_package_data=True, - install_requires=( - "google-auth >= 0.4.0", - "click>=7.0.0", - "packaging>=19.0", - "importlib_metadata>=1.0.0; python_version<'3.8'", - ), - python_requires=">=3.7", - classifiers=[ - "Development Status :: 4 - Beta", - "Intended Audience :: Developers", - "Operating System :: OS Independent", - "Programming Language :: Python :: 3", - "Programming Language :: Python :: 3.7", - "Programming Language :: Python :: 3.8", - "Programming Language :: Python :: 3.9", - "Programming Language :: Python :: 3.10", - "Programming Language :: Python :: 3.11", - "Programming Language :: Python :: 3.12", - "Programming Language :: Python :: 3.13", - "Programming Language :: Python :: 3.14", - "Topic :: Internet", - "Topic :: Software Development :: Libraries :: Python Modules", - ], - zip_safe=False, -) diff --git a/test_utils/__init__.py b/test_utils/__init__.py deleted file mode 100644 index 8a4b251..0000000 --- a/test_utils/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2025 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. -# - -from .version import __version__ # noqa: F401 diff --git a/test_utils/imports.py b/test_utils/imports.py deleted file mode 100644 index 46489b3..0000000 --- a/test_utils/imports.py +++ /dev/null @@ -1,38 +0,0 @@ -# Copyright 2019 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. - -import builtins -from unittest import mock - - -def maybe_fail_import(predicate): - """Create and return a patcher that conditionally makes an import fail. - - Args: - predicate (Callable[[...], bool]): A callable that, if it returns `True`, - triggers an `ImportError`. It must accept the same arguments as the - built-in `__import__` function. - https://docs.python.org/3/library/functions.html#__import__ - - Returns: - A mock patcher object that can be used to enable patched import behavior. - """ - orig_import = builtins.__import__ - - def custom_import(name, globals=None, locals=None, fromlist=(), level=0): - if predicate(name, globals, locals, fromlist, level): - raise ImportError - return orig_import(name, globals, locals, fromlist, level) - - return mock.patch.object(builtins, "__import__", new=custom_import) diff --git a/test_utils/lower_bound_checker/__init__.py b/test_utils/lower_bound_checker/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/test_utils/lower_bound_checker/lower_bound_checker.py b/test_utils/lower_bound_checker/lower_bound_checker.py deleted file mode 100644 index b6594de..0000000 --- a/test_utils/lower_bound_checker/lower_bound_checker.py +++ /dev/null @@ -1,271 +0,0 @@ -# Copyright 2021 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. - -import click -from pathlib import Path -from typing import List, Tuple, Set -import sys - -from packaging.requirements import Requirement -from packaging.version import Version - -if sys.version_info < (3, 8): - import importlib_metadata as metadata -else: - import importlib.metadata as metadata - - -def _get_package_requirements(package_name: str) -> List[Requirement]: - """ - Get a list of all requirements and extras declared by this package. - The package must already be installed in the environment. - - Args: - package_name (str): The name of the package. - - Returns: - List[packaging.requirements.Requirement]: A list of package requirements and extras. - """ - requirements = [] - distribution = metadata.distribution(package_name) - if distribution.requires: - requirements = [Requirement(str(r)) for r in distribution.requires] - return requirements - - -def _parse_requirements_file(requirements_file: str) -> List[Requirement]: - """ - Get a list of requirements found in a requirements file. - - Args: - requirements_file (str): Path to a requirements file. - - Returns: - List[Requirement]: A list of requirements. - """ - requirements = [] - - with Path(requirements_file).open() as f: - for line in f: - line = line.strip() - if line and not line.startswith("#"): - requirements.append(Requirement(line)) - - return requirements - - -def _get_pinned_versions( - ctx: click.Context, requirements: List[Requirement] -) -> Set[Tuple[str, Version]]: - """Turn a list of requirements into a set of (package name, Version) tuples. - - The requirements are all expected to pin explicitly to one version. - Other formats will result in an error. - - {("requests", Version("1.25.0"), ("google-auth", Version("1.0.0")} - - Args: - ctx (click.Context): The current click context. - requirements (List[Requirement]): A list of requirements. - - Returns: - Set[Tuple[str, Version]]: Tuples of the package name and Version. - """ - constraints = set() - - invalid_requirements = [] - - for constraint in requirements: - spec_set = list(constraint.specifier) - if len(spec_set) != 1: - invalid_requirements.append(constraint.name) - else: - if spec_set[0].operator != "==": - invalid_requirements.append(constraint.name) - else: - constraints.add((constraint.name, Version(spec_set[0].version))) - - if invalid_requirements: - ctx.fail( - f"These requirements are not pinned to one version: {invalid_requirements}" - ) - - return constraints - - -class IndeterminableLowerBound(Exception): - pass - - -def _lower_bound(requirement: Requirement) -> str: - """ - Given a requirement, determine the lowest version that fulfills the requirement. - The lower bound can be determined for a requirement only if it is one of these - formats: - - foo==1.2.0 - foo>=1.2.0 - foo>=1.2.0, <2.0.0dev - foo<2.0.0dev, >=1.2.0 - - Args: - requirement (Requirement): A requirement to parse - - Returns: - str: The lower bound for the requirement. - """ - spec_set = list(requirement.specifier) - - # sort by operator: <, then >= - spec_set.sort(key=lambda x: x.operator) - - if len(spec_set) == 1: - # foo==1.2.0 - if spec_set[0].operator == "==": - return spec_set[0].version - # foo>=1.2.0 - elif spec_set[0].operator == ">=": - return spec_set[0].version - # foo<2.0.0, >=1.2.0 or foo>=1.2.0, <2.0.0 - elif len(spec_set) == 2: - if spec_set[0].operator == "<" and spec_set[1].operator == ">=": - return spec_set[1].version - - raise IndeterminableLowerBound( - f"Lower bound could not be determined for {requirement.name}" - ) - - -def _get_package_lower_bounds( - ctx: click.Context, requirements: List[Requirement] -) -> Set[Tuple[str, Version]]: - """Get a set of tuples ('package_name', Version('1.0.0')) from a - list of Requirements. - - Args: - ctx (click.Context): The current click context. - requirements (List[Requirement]): A list of requirements. - - Returns: - Set[Tuple[str, Version]]: A set of (package_name, lower_bound) - tuples. - """ - bad_package_lower_bounds = [] - package_lower_bounds = set() - - for req in requirements: - try: - version = _lower_bound(req) - package_lower_bounds.add((req.name, Version(version))) - except IndeterminableLowerBound: - bad_package_lower_bounds.append(req.name) - - if bad_package_lower_bounds: - ctx.fail( - f"setup.py is missing explicit lower bounds for the following packages: {str(bad_package_lower_bounds)}" - ) - else: - return package_lower_bounds - - -@click.group() -def main(): - pass - - -@main.command() -@click.option("--package-name", required=True, help="Name of the package.") -@click.option("--constraints-file", required=True, help="Path to constraints file.") -@click.pass_context -def update(ctx: click.Context, package_name: str, constraints_file: str) -> None: - """Create a constraints file with lower bounds for package-name. - - If the constraints file already exists the contents will be overwritten. - """ - requirements = _get_package_requirements(package_name) - requirements.sort(key=lambda x: x.name) - - package_lower_bounds = list(_get_package_lower_bounds(ctx, requirements)) - package_lower_bounds.sort(key=lambda x: x[0]) - - constraints = [f"{name}=={version}" for name, version in package_lower_bounds] - Path(constraints_file).write_text("\n".join(constraints)) - - -@main.command() -@click.option("--package-name", required=True, help="Name of the package.") -@click.option("--constraints-file", required=True, help="Path to constraints file.") -@click.pass_context -def check(ctx: click.Context, package_name: str, constraints_file: str): - """Check that the constraints-file pins to the lower bound specified in package-name's - setup.py for each requirement. - - Requirements: - - 1. The setup.py pins every requirement in one of the following formats: - - * foo==1.2.0 - - * foo>=1.2.0 - - * foo>=1.2.0, <2.0.0dev - - * foo<2.0.0dev, >=1.2.0 - - 2. The constraints file pins every requirement to a single version: - - * foo==1.2.0 - - 3. package-name is already installed in the environment. - """ - - package_requirements = _get_package_requirements(package_name) - constraints = _parse_requirements_file(constraints_file) - - package_lower_bounds = _get_package_lower_bounds(ctx, package_requirements) - constraints_file_versions = _get_pinned_versions(ctx, constraints) - - # Look for dependencies in setup.py that are missing from constraints.txt - package_names = {x[0] for x in package_lower_bounds} - constraint_names = {x[0] for x in constraints_file_versions} - missing_from_constraints = package_names - constraint_names - - if missing_from_constraints: - ctx.fail( - ( - f"The following packages are declared as a requirement or extra" - f"in setup.py but were not found in {constraints_file}: {str(missing_from_constraints)}" - ) - ) - - # We use .issuperset() instead of == because there may be additional entries - # in constraints.txt (e.g., test only requirements) - if not constraints_file_versions.issuperset(package_lower_bounds): - first_line = f"The following packages have different versions {package_name}'s setup.py and {constraints_file}" - error_msg = [first_line, "-" * (7 + len(first_line))] - - difference = package_lower_bounds - constraints_file_versions - constraints_dict = dict(constraints_file_versions) - - for req, setup_py_version in difference: - error_msg.append( - f"'{req}' lower bound is {setup_py_version} in setup.py but constraints file has {constraints_dict[req]}" - ) - ctx.fail("\n".join(error_msg)) - - click.secho("All good!", fg="green") - - -if __name__ == "__main__": - main() diff --git a/test_utils/orchestrate.py b/test_utils/orchestrate.py deleted file mode 100644 index a6fd9a7..0000000 --- a/test_utils/orchestrate.py +++ /dev/null @@ -1,446 +0,0 @@ -# Copyright 2018 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 itertools -import math -import queue -import sys -import threading -import tokenize - - -def orchestrate(*tests, **kwargs): - """ - Orchestrate a deterministic concurrency test. - - Runs test functions in separate threads, with each thread taking turns running up - until predefined syncpoints in a deterministic order. All possible orderings are - tested. - - Most of the time, we try to use logic, best practices, and static analysis to insure - correct operation of concurrent code. Sometimes our powers of reasoning fail us and, - either through non-determistic stress testing or running code in production, a - concurrent bug is discovered. When this occurs, we'd like to have a regression test - to insure we've understood the problem and implemented a correct solution. - `orchestrate` provides a means of deterministically testing concurrent code so we - can write robust regression tests for complex concurrent scenarios. - - `orchestrate` runs each passed in test function in its own thread. Threads then - "take turns" running. Turns are defined by setting syncpoints in the code under - test, using comments containing "pragma: SYNCPOINT". `orchestrate` will scan the - code under test and add syncpoints where it finds these comments. - - For example, let's say you have the following code in production:: - - def hither_and_yon(destination): - hither(destination) - yon(destination) - - You've found there's a concurrency bug when two threads execute this code with the - same argument, and you think that by adding a syncpoint between the calls to - `hither` and `yon` you can reproduce the problem in a regression test. First add a - comment with "pragma: SYNCPOINT" to the code under test:: - - def hither_and_yon(destination): - hither(destination) # pragma: SYNCPOINT - yon(destination) - - When testing with orchestrate, there will now be a syncpoint, or a pause, after the - call to `hither` and before the call to `yon`. Now you can write a test to exercise - `hither_and_yon` running in parallel:: - - from unittest import mock - from tests.unit import orchestrate - - from myorg.myproj.sales import travel - - def test_concurrent_hither_and_yon(): - - def test_hither_and_yon(): - assert something - travel.hither_and_yon("Raleigh") - assert something_else - - counts = orchestrate.orchestrate(test_hither_and_yon, test_hither_and_yon) - assert counts == (2, 2) - - What `orchestrate` will do now is take each of the two test functions passed in - (actually the same function, twice, in this case), run them serially, and count the - number of turns it takes to run each test to completion. In this example, it will - take two turns for each test: one turn to start the thread and execute up until the - syncpoint, and then another turn to execute from the syncpoint to the end of the - test. The number of turns will always be one greater than the number of syncpoints - encountered when executing the test. - - Once the counts have been taken, `orchestrate` will construct a test sequence that - represents all of the turns taken by the passed in tests, with each value in the - sequence representing the index of the test whose turn it is in the sequence. In - this example, then, it would produce:: - - [0, 0, 1, 1] - - This represents the first test taking both of its turns, followed by the second test - taking both of its turns. At this point this scenario has already been tested, - because this is what was run to produce the counts and the initial test sequence. - Now `orchestrate` will run all of the remaining scenarios by finding all the - permutations of the test sequence and executing those, in turn:: - - [0, 1, 0, 1] - [0, 1, 1, 0] - [1, 0, 0, 1] - [1, 0, 1, 0] - [1, 1, 0, 0] - - You'll notice in our example that since both test functions are actually the same - function, that although it tested 6 scenarios there are effectively only really 3 - unique scenarios. For the time being, though, `orchestrate` doesn't attempt to - detect this condition or optimize for it. - - There are some performance considerations that should be taken into account when - writing tests. The number of unique test sequences grows quite quickly with the - number of turns taken by the functions under test. Our simple example with two - threads each taking two turns, only yielded 6 scenarios, but two threads each taking - 6 turns, for example, yields 924 scenarios. Add another six step thread, for a total - of three threads, and now you have over 17 thousand scenarios. In general, use the - least number of steps/threads you can get away with and still expose the behavior - you want to correct. - - For the same reason as above, it is recommended that if you have many concurrent - tests, that you name your syncpoints so that you're not accidentally using - syncpoints intended for other tests, as this will add steps to your tests. While - it's not problematic from a testing standpoint to have extra steps in your tests, it - can use computing resources unnecessarily. A name can be added to any syncpoint - after the `SYNCPOINT` keyword in the pragma definition:: - - def hither_and_yon(destination): - hither(destination) # pragma: SYNCPOINT hither and yon - yon(destination) - - In your test, then, pass that name to `orchestrate` to cause it to use only - syncpoints with that name:: - - orchestrate.orchestrate( - test_hither_and_yon, test_hither_and_yon, name="hither and yon" - ) - - As soon as any error or failure is detected, no more scenarios are run - and that error is propagated to the main thread. - - One limitation of `orchestrate` is that it cannot really be used with `coverage`, - since both tools use `sys.set_trace`. Any code that needs verifiable test coverage - should have additional tests that do not use `orchestrate`, since code that is run - under orchestrate will not show up in a coverage report generated by `coverage`. - - Args: - tests (Tuple[Callable]): Test functions to be run. These functions will not be - called with any arguments, so they must not have any required arguments. - name (Optional[str]): Only use syncpoints with the given name. If omitted, only - unnamed syncpoints will be used. - - Returns: - Tuple[int]: A tuple of the count of the number turns for test passed in. Can be - used a sanity check in tests to make sure you understand what's actually - happening during a test. - """ - name = kwargs.pop("name", None) - if kwargs: - raise TypeError( - "Unexpected keyword arguments: {}".format(", ".join(kwargs.keys())) - ) - - # Produce an initial test sequence. The fundamental question we're always trying to - # answer is "whose turn is it?" First we'll find out how many "turns" each test - # needs to complete when run serially and use that to construct a sequence of - # indexes. When a test's index appears in the sequence, it is that test's turn to - # run. We'll start by constructing a sequence that would run each test through to - # completion serially, one after the other. - test_sequence = [] - counts = [] - for index, test in enumerate(tests): - thread = _TestThread(test, name) - for count in itertools.count(1): # pragma: NO BRANCH - # Pragma is required because loop never finishes naturally. - thread.go() - if thread.finished: - break - - counts.append(count) - test_sequence += [index] * count - - # Now we can take that initial sequence and generate all of its permutations, - # running each one to try to uncover concurrency bugs - sequences = iter(_permutations(test_sequence)) - - # We already tested the first sequence getting our counts, so we can discard it - next(sequences) - - # Test each sequence - for test_sequence in sequences: - threads = [_TestThread(test, name) for test in tests] - try: - for index in test_sequence: - threads[index].go() - - # Its possible for number of turns to vary from one test run to the other, - # especially if there is some undiscovered concurrency bug. Go ahead and - # finish running each test to completion, if not already complete. - for thread in threads: - while not thread.finished: - thread.go() - - except Exception: - # If an exception occurs, we still need to let any threads that are still - # going finish up. Additional exceptions are silently ignored. - for thread in threads: - thread.finish() - raise - - return tuple(counts) - - -_local = threading.local() - - -class _Conductor: - """Coordinate communication between main thread and a test thread. - - Two way communicaton is maintained between the main thread and a test thread using - two synchronized queues (`queue.Queue`) each with a size of one. - """ - - def __init__(self): - self._notify = queue.Queue(1) - self._go = queue.Queue(1) - - def notify(self): - """Called from test thread to let us know it's finished or is ready for its next - turn.""" - self._notify.put(None) - - def standby(self): - """Called from test thread in order to block until told to go.""" - self._go.get() - - def wait(self): - """Called from main thread to wait for test thread to either get to the - next syncpoint or finish.""" - self._notify.get() - - def go(self): - """Called from main thread to tell test thread to go.""" - self._go.put(None) - - -_SYNCPOINTS = {} -"""Dict[str, Dict[str, Set[int]]]: Dict mapping source fileneme to a dict mapping -syncpoint name to set of line numbers where syncpoints with that name occur in the -source file. -""" - - -def _get_syncpoints(filename): - """Find syncpoints in a source file. - - Does a simple tokenization of the source file, looking for comments with "pragma: - SYNCPOINT", and populates _SYNCPOINTS using the syncpoint name and line number in - the source file. - """ - _SYNCPOINTS[filename] = syncpoints = {} - - # Use tokenize to find pragma comments - with open(filename, "r") as pyfile: - tokens = tokenize.generate_tokens(pyfile.readline) - for type, value, start, end, line in tokens: - if type == tokenize.COMMENT and "pragma: SYNCPOINT" in value: - name = value.split("SYNCPOINT", 1)[1].strip() - if not name: - name = None - - if name not in syncpoints: - syncpoints[name] = set() - - lineno, column = start - syncpoints[name].add(lineno) - - -class _TestThread: - """A thread for a test function.""" - - thread = None - finished = False - error = None - at_syncpoint = False - - def __init__(self, test, name): - self.test = test - self.name = name - self.conductor = _Conductor() - - def _run(self): - sys.settrace(self._trace) - _local.conductor = self.conductor - try: - self.test() - except Exception as error: - self.error = error - finally: - self.finished = True - self.conductor.notify() - - def _sync(self): - # Tell main thread we're finished, for now - self.conductor.notify() - - # Wait for the main thread to tell us to go again - self.conductor.standby() - - def _trace(self, frame, event, arg): - """Argument to `sys.settrace`. - - Handles frames during test run, syncing at syncpoints, when found. - - Returns: - `None` if no more tracing is required for the function call, `self._trace` - if tracing should continue. - """ - if self.at_syncpoint: - # We hit a syncpoint on the previous call, so now we sync. - self._sync() - self.at_syncpoint = False - - filename = frame.f_globals.get("__file__") - if not filename: - # Can't trace code without a source file - return - - if filename.endswith(".pyc"): - filename = filename[:-1] - - if filename not in _SYNCPOINTS: - _get_syncpoints(filename) - - syncpoints = _SYNCPOINTS[filename].get(self.name) - if not syncpoints: - # This file doesn't contain syncpoints, don't continue to trace - return - - # We've hit a syncpoint. Execute whatever line the syncpoint is on and then - # sync next time this gets called. - if frame.f_lineno in syncpoints: - self.at_syncpoint = True - - return self._trace - - def go(self): - if self.finished: - return - - if self.thread is None: - self.thread = threading.Thread(target=self._run) - self.thread.start() - - else: - self.conductor.go() - - self.conductor.wait() - - if self.error: - raise self.error - - def finish(self): - while not self.finished: - try: - self.go() - except Exception: - pass - - -class _permutations: - """Generates a sequence of all permutations of `sequence`. - - Permutations are returned in lexicographic order using the "Generation in - lexicographic order" algorithm described in `the Wikipedia article on "Permutation" - `_. - - This implementation differs significantly from `itertools.permutations` in that the - value of individual elements is taken into account, thus eliminating redundant - orderings that would be produced by `itertools.permutations`. - - Args: - sequence (Sequence[Any]): Sequence must be finite and orderable. - - Returns: - Sequence[Sequence[Any]]: Set of all permutations of `sequence`. - """ - - def __init__(self, sequence): - self._start = tuple(sorted(sequence)) - - def __len__(self): - """Compute the number of permutations. - - Let the number of elements in a sequence N and the number of repetitions for - individual members of the sequence be n1, n2, ... nx. The number of unique - permutations is: N! / n1! / n2! / ... / nx!. - - For example, let `sequence` be [1, 2, 3, 1, 2, 3, 1, 2, 3]. The number of unique - permutations is: 9! / 3! / 3! / 3! = 1680. - - See: "Permutations of multisets" in `the Wikipedia article on "Permutation" - `_. - """ - repeats = [len(list(group)) for value, group in itertools.groupby(self._start)] - length = math.factorial(len(self._start)) - for repeat in repeats: - length /= math.factorial(repeat) - - return int(length) - - def __iter__(self): - """Iterate over permutations. - - See: "Generation in lexicographic order" algorithm described in `the Wikipedia - article on "Permutation" `_. - """ - current = list(self._start) - size = len(current) - - while True: - yield tuple(current) - - # 1. Find the largest index i such that a[i] < a[i + 1]. - for i in range(size - 2, -1, -1): - if current[i] < current[i + 1]: - break - - else: - # If no such index exists, the permutation is the last permutation. - return - - # 2. Find the largest index j greater than i such that a[i] < a[j]. - for j in range(size - 1, i, -1): - if current[i] < current[j]: - break - - else: # pragma: NO COVER - raise RuntimeError("Broken algorithm") - - # 3. Swap the value of a[i] with that of a[j]. - temp = current[i] - current[i] = current[j] - current[j] = temp - - # 4. Reverse the sequence from a[i + 1] up to and including the final - # element a[n]. - current = current[: i + 1] + list(reversed(current[i + 1 :])) diff --git a/test_utils/prefixer.py b/test_utils/prefixer.py deleted file mode 100644 index 89d1e8e..0000000 --- a/test_utils/prefixer.py +++ /dev/null @@ -1,82 +0,0 @@ -# Copyright 2021 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. - -import datetime -import random -import re - -from typing import Union - -_RESOURCE_DATE_FORMAT = "%Y%m%d%H%M%S" -_RESOURCE_DATE_LENGTH = 4 + 2 + 2 + 2 + 2 + 2 -_RE_SEPARATORS = re.compile(r"[/\-\\_]") - - -def _common_prefix(repo, relative_dir, separator="_"): - repo = _RE_SEPARATORS.sub(separator, repo) - relative_dir = _RE_SEPARATORS.sub(separator, relative_dir) - return f"{repo}{separator}{relative_dir}" - - -class Prefixer(object): - """Create/manage resource IDs for system testing. - - Usage: - - Creating resources: - - >>> import test_utils.prefixer - >>> prefixer = test_utils.prefixer.Prefixer("python-bigquery", "samples/snippets") - >>> dataset_id = prefixer.create_prefix() + "my_sample" - - Cleaning up resources: - - >>> @pytest.fixture(scope="session", autouse=True) - ... def cleanup_datasets(bigquery_client: bigquery.Client): - ... for dataset in bigquery_client.list_datasets(): - ... if prefixer.should_cleanup(dataset.dataset_id): - ... bigquery_client.delete_dataset( - ... dataset, delete_contents=True, not_found_ok=True - """ - - def __init__( - self, repo, relative_dir, separator="_", cleanup_age=datetime.timedelta(days=1) - ): - self._separator = separator - self._cleanup_age = cleanup_age - self._prefix = _common_prefix(repo, relative_dir, separator=separator) - - def create_prefix(self) -> str: - now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) - timestamp = now.strftime(_RESOURCE_DATE_FORMAT) - random_string = hex(random.randrange(0x1000000))[2:] - return f"{self._prefix}{self._separator}{timestamp}{self._separator}{random_string}" - - def _name_to_date(self, resource_name: str) -> Union[datetime.datetime, None]: - start_date = len(self._prefix) + len(self._separator) - date_string = resource_name[start_date : start_date + _RESOURCE_DATE_LENGTH] - try: - parsed_date = datetime.datetime.strptime(date_string, _RESOURCE_DATE_FORMAT) - return parsed_date - except ValueError: - return None - - def should_cleanup(self, resource_name: str) -> bool: - now = datetime.datetime.now(datetime.timezone.utc).replace(tzinfo=None) - yesterday = now - self._cleanup_age - if not resource_name.startswith(self._prefix): - return False - - created_date = self._name_to_date(resource_name) - return created_date is not None and created_date < yesterday diff --git a/test_utils/py.typed b/test_utils/py.typed deleted file mode 100644 index 7f6cc03..0000000 --- a/test_utils/py.typed +++ /dev/null @@ -1,2 +0,0 @@ -# Marker file for PEP 561. -# The test_utils package uses inline types. diff --git a/test_utils/retry.py b/test_utils/retry.py deleted file mode 100644 index a84a900..0000000 --- a/test_utils/retry.py +++ /dev/null @@ -1,228 +0,0 @@ -# Copyright 2016 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. - -import functools -import time - -MAX_TRIES = 4 -DELAY = 1 -BACKOFF = 2 - - -def _retry_all(_): - """Retry all caught exceptions.""" - return True - - -class BackoffFailed(Exception): - """Retry w/ backoffs did not complete successfully.""" - - -class RetryBase(object): - """Base for retrying calling a decorated function w/ exponential backoff. - - :type max_tries: int - :param max_tries: Number of times to try (not retry) before giving up. - - :type delay: int - :param delay: Initial delay between retries in seconds. - - :type backoff: int - :param backoff: Backoff multiplier e.g. value of 2 will double the - delay each retry. - - :type logger: logging.Logger instance - :param logger: Logger to use. If None, print. - """ - - def __init__(self, max_tries=MAX_TRIES, delay=DELAY, backoff=BACKOFF, logger=None): - self.max_tries = max_tries - self.delay = delay - self.backoff = backoff - self.logger = logger.warning if logger else print - - -class RetryErrors(RetryBase): - """Decorator for retrying given exceptions in testing. - - :type exception: Exception or tuple of Exceptions - :param exception: The exception to check or may be a tuple of - exceptions to check. - - :type error_predicate: function, takes caught exception, returns bool - :param error_predicate: Predicate evaluating whether to retry after a - caught exception. - - :type max_tries: int - :param max_tries: Number of times to try (not retry) before giving up. - - :type delay: int - :param delay: Initial delay between retries in seconds. - - :type backoff: int - :param backoff: Backoff multiplier e.g. value of 2 will double the - delay each retry. - - :type logger: logging.Logger instance - :param logger: Logger to use. If None, print. - """ - - def __init__( - self, - exception, - error_predicate=_retry_all, - max_tries=MAX_TRIES, - delay=DELAY, - backoff=BACKOFF, - logger=None, - ): - super(RetryErrors, self).__init__(max_tries, delay, backoff, logger) - self.exception = exception - self.error_predicate = error_predicate - - def __call__(self, to_wrap): - @functools.wraps(to_wrap) - def wrapped_function(*args, **kwargs): - tries = 0 - while tries < self.max_tries: - try: - return to_wrap(*args, **kwargs) - except self.exception as caught_exception: - if not self.error_predicate(caught_exception): - raise - - delay = self.delay * self.backoff**tries - msg = "%s, Trying again in %d seconds..." % ( - caught_exception, - delay, - ) - self.logger(msg) - - time.sleep(delay) - tries += 1 - return to_wrap(*args, **kwargs) - - return wrapped_function - - -class RetryResult(RetryBase): - """Decorator for retrying based on non-error result. - - :type result_predicate: function, takes result, returns bool - :param result_predicate: Predicate evaluating whether to retry after a - result is returned. - - :type max_tries: int - :param max_tries: Number of times to try (not retry) before giving up. - - :type delay: int - :param delay: Initial delay between retries in seconds. - - :type backoff: int - :param backoff: Backoff multiplier e.g. value of 2 will double the - delay each retry. - - :type logger: logging.Logger instance - :param logger: Logger to use. If None, print. - """ - - def __init__( - self, - result_predicate, - max_tries=MAX_TRIES, - delay=DELAY, - backoff=BACKOFF, - logger=None, - ): - super(RetryResult, self).__init__(max_tries, delay, backoff, logger) - self.result_predicate = result_predicate - - def __call__(self, to_wrap): - @functools.wraps(to_wrap) - def wrapped_function(*args, **kwargs): - tries = 0 - while tries < self.max_tries: - result = to_wrap(*args, **kwargs) - if self.result_predicate(result): - return result - - delay = self.delay * self.backoff**tries - msg = "%s. Trying again in %d seconds..." % ( - self.result_predicate.__name__, - delay, - ) - self.logger(msg) - - time.sleep(delay) - tries += 1 - raise BackoffFailed() - - return wrapped_function - - -class RetryInstanceState(RetryBase): - """Decorator for retrying based on instance state. - - :type instance_predicate: function, takes instance, returns bool - :param instance_predicate: Predicate evaluating whether to retry after an - API-invoking method is called. - - :type max_tries: int - :param max_tries: Number of times to try (not retry) before giving up. - - :type delay: int - :param delay: Initial delay between retries in seconds. - - :type backoff: int - :param backoff: Backoff multiplier e.g. value of 2 will double the - delay each retry. - - :type logger: logging.Logger instance - :param logger: Logger to use. If None, print. - """ - - def __init__( - self, - instance_predicate, - max_tries=MAX_TRIES, - delay=DELAY, - backoff=BACKOFF, - logger=None, - ): - super(RetryInstanceState, self).__init__(max_tries, delay, backoff, logger) - self.instance_predicate = instance_predicate - - def __call__(self, to_wrap): - instance = to_wrap.__self__ # only instance methods allowed - - @functools.wraps(to_wrap) - def wrapped_function(*args, **kwargs): - tries = 0 - while tries < self.max_tries: - result = to_wrap(*args, **kwargs) - if self.instance_predicate(instance): - return result - - delay = self.delay * self.backoff**tries - msg = "%s. Trying again in %d seconds..." % ( - self.instance_predicate.__name__, - delay, - ) - self.logger(msg) - - time.sleep(delay) - tries += 1 - raise BackoffFailed() - - return wrapped_function diff --git a/test_utils/system.py b/test_utils/system.py deleted file mode 100644 index 18a2930..0000000 --- a/test_utils/system.py +++ /dev/null @@ -1,80 +0,0 @@ -# Copyright 2014 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. - -from __future__ import print_function -import os -import sys -import time - -import google.auth.credentials # type: ignore -from google.auth.environment_vars import CREDENTIALS as TEST_CREDENTIALS # type: ignore - - -# From shell environ. May be None. -CREDENTIALS = os.getenv(TEST_CREDENTIALS) - -ENVIRON_ERROR_MSG = """\ -To run the system tests, you need to set some environment variables. -Please check the CONTRIBUTING guide for instructions. -""" - - -class EmulatorCreds(google.auth.credentials.Credentials): - """A mock credential object. - - Used to avoid unnecessary token refreshing or reliance on the network - while an emulator is running. - """ - - def __init__(self): # pylint: disable=super-init-not-called - self.token = b"seekrit" - self.expiry = None - - @property - def valid(self): - """Would-be validity check of the credentials. - - Always is :data:`True`. - """ - return True - - def refresh(self, unused_request): # pylint: disable=unused-argument - """Off-limits implementation for abstract method.""" - raise RuntimeError("Should never be refreshed.") - - -def check_environ(): - err_msg = None - if CREDENTIALS is None: - err_msg = "\nMissing variables: " + TEST_CREDENTIALS - elif not os.path.isfile(CREDENTIALS): - err_msg = "\nThe %s path %r is not a file." % (TEST_CREDENTIALS, CREDENTIALS) - - if err_msg is not None: - msg = ENVIRON_ERROR_MSG + err_msg - print(msg, file=sys.stderr) - sys.exit(1) - - -def unique_resource_id(delimiter="_"): - """A unique identifier for a resource. - - Intended to help locate resources created in particular - testing environments and at particular times. - """ - build_id = os.getenv("CIRCLE_BUILD_NUM", "") - if build_id == "": - return "%s%d" % (delimiter, 1000 * time.time()) - else: - return "%s%s%s%d" % (delimiter, build_id, delimiter, time.time()) diff --git a/test_utils/version.py b/test_utils/version.py deleted file mode 100644 index 3ed5ae3..0000000 --- a/test_utils/version.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright 2025 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. -# -__version__ = "1.7.0" diff --git a/test_utils/vpcsc_config.py b/test_utils/vpcsc_config.py deleted file mode 100644 index c5e36e7..0000000 --- a/test_utils/vpcsc_config.py +++ /dev/null @@ -1,118 +0,0 @@ -# -*- coding: utf-8 -*- -# -# Copyright 2019 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 os - -import pytest # type: ignore - - -INSIDE_VPCSC_ENVVAR = "GOOGLE_CLOUD_TESTS_IN_VPCSC" -PROJECT_INSIDE_ENVVAR = "PROJECT_ID" -PROJECT_OUTSIDE_ENVVAR = "GOOGLE_CLOUD_TESTS_VPCSC_OUTSIDE_PERIMETER_PROJECT" -BUCKET_OUTSIDE_ENVVAR = "GOOGLE_CLOUD_TESTS_VPCSC_OUTSIDE_PERIMETER_BUCKET" - - -class VPCSCTestConfig(object): - """System test utility for VPCSC detection. - - See: https://cloud.google.com/vpc-service-controls/docs/ - """ - - @property - def inside_vpcsc(self): - """Test whether the test environment is configured to run inside VPCSC. - - Returns: - bool: - true if the environment is configured to run inside VPCSC, - else false. - """ - return INSIDE_VPCSC_ENVVAR in os.environ - - @property - def project_inside(self): - """Project ID for testing outside access. - - Returns: - str: project ID used for testing outside access; None if undefined. - """ - return os.environ.get(PROJECT_INSIDE_ENVVAR, None) - - @property - def project_outside(self): - """Project ID for testing inside access. - - Returns: - str: project ID used for testing inside access; None if undefined. - """ - return os.environ.get(PROJECT_OUTSIDE_ENVVAR, None) - - @property - def bucket_outside(self): - """GCS bucket for testing inside access. - - Returns: - str: bucket ID used for testing inside access; None if undefined. - """ - return os.environ.get(BUCKET_OUTSIDE_ENVVAR, None) - - def skip_if_inside_vpcsc(self, testcase): - """Test decorator: skip if running inside VPCSC.""" - reason = ( - "Running inside VPCSC. " - "Unset the {} environment variable to enable this test." - ).format(INSIDE_VPCSC_ENVVAR) - skip = pytest.mark.skipif(self.inside_vpcsc, reason=reason) - return skip(testcase) - - def skip_unless_inside_vpcsc(self, testcase): - """Test decorator: skip if running outside VPCSC.""" - reason = ( - "Running outside VPCSC. " - "Set the {} environment variable to enable this test." - ).format(INSIDE_VPCSC_ENVVAR) - skip = pytest.mark.skipif(not self.inside_vpcsc, reason=reason) - return skip(testcase) - - def skip_unless_inside_project(self, testcase): - """Test decorator: skip if inside project env var not set.""" - reason = ( - "Project ID for running inside VPCSC not set. " - "Set the {} environment variable to enable this test." - ).format(PROJECT_INSIDE_ENVVAR) - skip = pytest.mark.skipif(self.project_inside is None, reason=reason) - return skip(testcase) - - def skip_unless_outside_project(self, testcase): - """Test decorator: skip if outside project env var not set.""" - reason = ( - "Project ID for running outside VPCSC not set. " - "Set the {} environment variable to enable this test." - ).format(PROJECT_OUTSIDE_ENVVAR) - skip = pytest.mark.skipif(self.project_outside is None, reason=reason) - return skip(testcase) - - def skip_unless_outside_bucket(self, testcase): - """Test decorator: skip if outside bucket env var not set.""" - reason = ( - "Bucket ID for running outside VPCSC not set. " - "Set the {} environment variable to enable this test." - ).format(BUCKET_OUTSIDE_ENVVAR) - skip = pytest.mark.skipif(self.bucket_outside is None, reason=reason) - return skip(testcase) - - -vpcsc_config = VPCSCTestConfig() diff --git a/testing/.gitignore b/testing/.gitignore deleted file mode 100644 index b05fbd6..0000000 --- a/testing/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -test-env.sh -service-account.json -client-secrets.json \ No newline at end of file diff --git a/testing/constraints-3.10.txt b/testing/constraints-3.10.txt deleted file mode 100644 index e69de29..0000000 diff --git a/testing/constraints-3.11.txt b/testing/constraints-3.11.txt deleted file mode 100644 index e69de29..0000000 diff --git a/testing/constraints-3.12.txt b/testing/constraints-3.12.txt deleted file mode 100644 index e69de29..0000000 diff --git a/testing/constraints-3.13.txt b/testing/constraints-3.13.txt deleted file mode 100644 index e69de29..0000000 diff --git a/testing/constraints-3.14.txt b/testing/constraints-3.14.txt deleted file mode 100644 index e69de29..0000000 diff --git a/testing/constraints-3.7.txt b/testing/constraints-3.7.txt deleted file mode 100644 index e694520..0000000 --- a/testing/constraints-3.7.txt +++ /dev/null @@ -1,4 +0,0 @@ -click==7.0.0 -google-auth==0.4.0 -importlib_metadata==1.0.0 -packaging==19.0 diff --git a/testing/constraints-3.8.txt b/testing/constraints-3.8.txt deleted file mode 100644 index e69de29..0000000 diff --git a/testing/constraints-3.9.txt b/testing/constraints-3.9.txt deleted file mode 100644 index e69de29..0000000 diff --git a/tests/unit/resources/bad_package/setup.py b/tests/unit/resources/bad_package/setup.py deleted file mode 100644 index cff27e1..0000000 --- a/tests/unit/resources/bad_package/setup.py +++ /dev/null @@ -1,40 +0,0 @@ -# Copyright 2021 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. - -import setuptools - - -requirements = [ - "requests", # no lower bound - "packaging>=14.0, !=15.0", # too complex for tool - "wheel<0.45.0", - "click==7.0.0", -] - -setuptools.setup( - name="invalid-package", - version="0.0.1", - author="Example Author", - author_email="author@example.com", - description="A small example package", - long_description_content_type="text/markdown", - url="https://github.com/pypa/sampleproject", - classifiers=[ - "Programming Language :: Python :: 3", - "Operating System :: OS Independent", - ], - install_requires=requirements, - packages=setuptools.find_packages(), - python_requires=">=3.7", -) diff --git a/tests/unit/resources/good_package/setup.py b/tests/unit/resources/good_package/setup.py deleted file mode 100644 index 2833216..0000000 --- a/tests/unit/resources/good_package/setup.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright 2021 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. - -import setuptools - - -# This package has four requirements. -# Each uses a different kind of pin accepted by the function that -# extracts lower bounds. -requirements = [ - "requests>=1.0.0", - "packaging>=14.0", - "wheel >=0.41.0", - "click==7.0.0", -] - -extras = {"grpc": "grpcio>=1.0.0"} - -setuptools.setup( - name="valid-package", - version="0.0.1", - author="Example Author", - author_email="author@example.com", - description="A small example package", - long_description_content_type="text/markdown", - url="https://github.com/pypa/sampleproject", - classifiers=[ - "Programming Language :: Python :: 3", - "Operating System :: OS Independent", - ], - install_requires=requirements, - extras_require=extras, - packages=setuptools.find_packages(), - python_requires=">=3.7", -) diff --git a/tests/unit/test_lower_bound_checker.py b/tests/unit/test_lower_bound_checker.py deleted file mode 100644 index 4cd467c..0000000 --- a/tests/unit/test_lower_bound_checker.py +++ /dev/null @@ -1,314 +0,0 @@ -# Copyright 2021 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. - -from contextlib import contextmanager -from pathlib import Path -import re -import tempfile -import sys -from typing import List - -from click.testing import CliRunner -import pytest # type: ignore - -if sys.version_info >= (3, 8): - import importlib.metadata as importlib_metadata -else: - # For Python 3.7 compatibility - import importlib_metadata - -from test_utils.lower_bound_checker import lower_bound_checker - -RUNNER = CliRunner() - -PACKAGE_LIST_REGEX = re.compile(r"Error.*[\[\{](.+)[\]\}]") -DIFFERENT_VERSIONS_LIST_REGEX = re.compile("'(.*?)' lower bound is") - -# These packages are installed into the environment by the nox session -# See 'resources/' for the setup.py files -GOOD_PACKAGE = "valid-package" -BAD_PACKAGE = "invalid-package" - - -def skip_test_if_not_installed(package_name: str): - """Skips the current test if given package is not installed""" - try: - importlib_metadata.distribution(package_name) - except importlib_metadata.PackageNotFoundError: - pytest.skip( - f"Skipping test which requires {package_name} in `tests/unit/resources/` to be installed" - ) - - -def parse_error_msg(msg: str) -> List[str]: - """Get package names from the error message. - - Example: - Error: setup.py is missing explicit lower bounds for the following packages: ["requests", "grpcio"] - """ - match = PACKAGE_LIST_REGEX.search(msg) - - reqs: List[str] = [] - - if match: - reqs = match.groups(1)[0].split(",") # type: ignore - reqs = [r.strip().replace("'", "").replace('"', "") for r in reqs] - - return reqs - - -def parse_diff_versions_error_msg(msg: str) -> List[str]: - """Get package names from the error message listing different versions - - Example: - 'requests' lower bound is 1.2.0 in setup.py but constraints file has 1.3.0 - 'grpcio' lower bound is 1.0.0 in setup.py but constraints file has 1.10.0 - """ - pattern = re.compile(DIFFERENT_VERSIONS_LIST_REGEX) - pkg_names = pattern.findall(msg) - - return pkg_names - - -@contextmanager -def constraints_file(requirements: List[str]): - """Write the list of requirements into a temporary file""" - - tmpdir = tempfile.TemporaryDirectory() - constraints_path = Path(tmpdir.name) / "constraints.txt" - - constraints_path.write_text("\n".join(requirements)) - yield constraints_path - - tmpdir.cleanup() - - -def test_update_constraints(): - skip_test_if_not_installed(GOOD_PACKAGE) - - with tempfile.TemporaryDirectory() as tmpdir: - constraints_path = Path(tmpdir) / "constraints.txt" - - result = RUNNER.invoke( - lower_bound_checker.update, - [ - "--package-name", - GOOD_PACKAGE, - "--constraints-file", - str(constraints_path), - ], - ) - - assert result.exit_code == 0 - assert constraints_path.exists() - - output = constraints_path.read_text().split("\n") - - assert output == [ - "click==7.0.0", - "grpcio==1.0.0", - "packaging==14.0", - "requests==1.0.0", - "wheel==0.41.0", - ] - - -def test_update_constraints_overwrites_existing_file(): - skip_test_if_not_installed(GOOD_PACKAGE) - - constraints = [ - "requests==1.0.0", - "packaging==13.0", - "wheel==0.42.0", - "click==5.0.0", - ] - with constraints_file(constraints) as c: - result = RUNNER.invoke( - lower_bound_checker.update, - ["--package-name", GOOD_PACKAGE, "--constraints-file", c], - ) - - assert result.exit_code == 0 - - output = c.read_text().split("\n") - assert output == [ - "click==7.0.0", - "grpcio==1.0.0", - "packaging==14.0", - "requests==1.0.0", - "wheel==0.41.0", - ] - - -def test_update_constraints_with_setup_py_missing_lower_bounds(): - skip_test_if_not_installed(BAD_PACKAGE) - - constraints = [ - "requests==1.0.0", - "packaging==14.0", - "wheel==0.41.0", - "click==7.0.0", - ] - with constraints_file(constraints) as c: - result = RUNNER.invoke( - lower_bound_checker.update, - ["--package-name", BAD_PACKAGE, "--constraints-file", c], - ) - - assert result.exit_code == 2 - assert "setup.py is missing explicit lower bounds" in result.output - - invalid_pkg_list = parse_error_msg(result.output) - assert set(invalid_pkg_list) == {"requests", "packaging", "wheel"} - - -def test_check(): - skip_test_if_not_installed(GOOD_PACKAGE) - - constraints = [ - "requests==1.0.0", - "packaging==14.0", - "wheel==0.41.0", - "click==7.0.0", - "grpcio==1.0.0", - ] - with constraints_file(constraints) as c: - result = RUNNER.invoke( - lower_bound_checker.check, - ["--package-name", GOOD_PACKAGE, "--constraints-file", c], - ) - - assert result.exit_code == 0 - - -def test_update_constraints_with_extra_constraints(): - skip_test_if_not_installed(GOOD_PACKAGE) - - constraints = [ - "requests==1.0.0", - "packaging==14.0", - "wheel==0.41.0", - "click==7.0.0", - "grpcio==1.0.0", - "pytest==6.0.0", # additional requirement - ] - with constraints_file(constraints) as c: - result = RUNNER.invoke( - lower_bound_checker.check, - ["--package-name", GOOD_PACKAGE, "--constraints-file", c], - ) - - assert result.exit_code == 0 - - -def test_check_with_missing_constraints_file(): - skip_test_if_not_installed(GOOD_PACKAGE) - - result = RUNNER.invoke( - lower_bound_checker.check, - [ - "--package-name", - GOOD_PACKAGE, - "--constraints-file", - "missing_constraints.txt", - ], - ) - - assert result.exit_code == 1 - assert isinstance(result.exception, FileNotFoundError) - - -def test_check_with_constraints_file_invalid_pins(): - skip_test_if_not_installed(GOOD_PACKAGE) - - constraints = [ - "requests==1.0.0", - "packaging==14.0", - "wheel==1.0.0, <2.0.0dev", # should be == - "click>=7.0.0", # should be == - ] - with constraints_file(constraints) as c: - result = RUNNER.invoke( - lower_bound_checker.check, - ["--package-name", GOOD_PACKAGE, "--constraints-file", c], - ) - - assert result.exit_code == 2 - - invalid_pkg_list = parse_error_msg(result.output) - - assert set(invalid_pkg_list) == {"wheel", "click"} - - -def test_check_with_constraints_file_missing_packages(): - skip_test_if_not_installed(GOOD_PACKAGE) - - constraints = [ - "requests==1.0.0", - "packaging==14.0", - # missing 'wheel' and 'click' and extra 'grpcio' - ] - with constraints_file(constraints) as c: - result = RUNNER.invoke( - lower_bound_checker.check, - ["--package-name", GOOD_PACKAGE, "--constraints-file", c], - ) - - assert result.exit_code == 2 - - invalid_pkg_list = parse_error_msg(result.output) - assert set(invalid_pkg_list) == {"wheel", "click", "grpcio"} - - -def test_check_with_constraints_file_different_versions(): - skip_test_if_not_installed(GOOD_PACKAGE) - - constraints = [ - "requests==1.2.0", # setup.py has 1.0.0 - "packaging==14.1", # setup.py has 14.0 - "wheel==0.42.0", # setup.py has 0.41.0 - "click==7.0.0", - "grpcio==1.0.0", - ] - with constraints_file(constraints) as c: - result = RUNNER.invoke( - lower_bound_checker.check, - ["--package-name", GOOD_PACKAGE, "--constraints-file", c], - ) - - assert result.exit_code == 2 - - invalid_pkg_list = parse_diff_versions_error_msg(result.output) - assert set(invalid_pkg_list) == {"requests", "packaging", "wheel"} - - -def test_check_with_setup_py_missing_lower_bounds(): - skip_test_if_not_installed(BAD_PACKAGE) - - constraints = [ - "requests==1.0.0", - "packaging==14.0", - "wheel==1.0.0", - "click==7.0.0", - ] - with constraints_file(constraints) as c: - result = RUNNER.invoke( - lower_bound_checker.check, - ["--package-name", BAD_PACKAGE, "--constraints-file", c], - ) - - assert result.exit_code == 2 - - invalid_pkg_list = parse_error_msg(result.output) - assert set(invalid_pkg_list) == {"requests", "packaging", "wheel"} diff --git a/tests/unit/test_orchestrate.py b/tests/unit/test_orchestrate.py deleted file mode 100644 index c11ce4e..0000000 --- a/tests/unit/test_orchestrate.py +++ /dev/null @@ -1,380 +0,0 @@ -# Copyright 2021 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 itertools -import threading - -try: - from unittest import mock -except ImportError: # pragma: NO PY3 COVER - import mock # type: ignore - -import pytest # type: ignore - -from test_utils import orchestrate - - -def test__permutations(): - sequence = [1, 2, 3, 1, 2, 3, 1, 2, 3] - permutations = orchestrate._permutations(sequence) - assert len(permutations) == 1680 - - result = list(permutations) - assert len(permutations) == len(result) # computed length matches reality - assert len(result) == len(set(result)) # no duplicates - assert result[0] == (1, 1, 1, 2, 2, 2, 3, 3, 3) - assert result[-1] == (3, 3, 3, 2, 2, 2, 1, 1, 1) - - assert list(orchestrate._permutations([1, 2, 3])) == [ - (1, 2, 3), - (1, 3, 2), - (2, 1, 3), - (2, 3, 1), - (3, 1, 2), - (3, 2, 1), - ] - - -class Test_orchestrate: - @staticmethod - def test_bad_keyword_argument(): - with pytest.raises(TypeError): - orchestrate.orchestrate(None, None, what="for?") - - @staticmethod - def test_no_failures(): - test_calls = [] - - def make_test(name): - def test(): # pragma: NO COVER - test_calls.append(name) # pragma: SYNCPOINT - test_calls.append(name) # pragma: SYNCPOINT - test_calls.append(name) - - return test - - test1 = make_test("A") - test2 = make_test("B") - - permutations = orchestrate._permutations(["A", "B", "A", "B", "A", "B"]) - expected = list(itertools.chain(*permutations)) - - counts = orchestrate.orchestrate(test1, test2) - assert counts == (3, 3) - assert test_calls == expected - - @staticmethod - def test_named_syncpoints(): - test_calls = [] - - def make_test(name): - def test(): # pragma: NO COVER - test_calls.append(name) # pragma: SYNCPOINT test_named_syncpoints - test_calls.append(name) # pragma: SYNCPOINT test_named_syncpoints - test_calls.append(name) # pragma: SYNCPOINT - - return test - - test1 = make_test("A") - test2 = make_test("B") - - permutations = orchestrate._permutations(["A", "B", "A", "B", "A", "B"]) - expected = list(itertools.chain(*permutations)) - - counts = orchestrate.orchestrate(test1, test2, name="test_named_syncpoints") - assert counts == (3, 3) - assert test_calls == expected - - @staticmethod - def test_syncpoints_decrease_after_initial_run(): - test_calls = [] - - def make_test(name): - syncpoints = [name] * 4 - - def test(): # pragma: NO COVER - test_calls.append(name) - if syncpoints: - syncpoints.pop() # pragma: SYNCPOINT - test_calls.append(name) - - return test - - test1 = make_test("A") - test2 = make_test("B") - - expected = [ - "A", - "A", - "B", - "B", - "A", - "B", - "A", - "B", - "A", - "B", - "B", - "A", - "B", - "A", - "A", - "B", - "B", - "A", - "B", - "A", - ] - - counts = orchestrate.orchestrate(test1, test2) - assert counts == (2, 2) - assert test_calls == expected - - @staticmethod - def test_syncpoints_increase_after_initial_run(): - test_calls = [] - - def do_nothing(): # pragma: NO COVER - pass - - def make_test(name): - syncpoints = [None] * 4 - - def test(): # pragma: NO COVER - test_calls.append(name) # pragma: SYNCPOINT - test_calls.append(name) - - if syncpoints: - syncpoints.pop() - else: - do_nothing() # pragma: SYNCPOINT - test_calls.append(name) - - return test - - test1 = make_test("A") - test2 = make_test("B") - - expected = [ - "A", - "A", - "B", - "B", - "A", - "B", - "A", - "B", - "A", - "B", - "B", - "A", - "B", - "A", - "A", - "B", - "B", - "A", - "B", - "A", - "A", - "B", - "B", - "B", - "A", - "A", - "A", - "B", - ] - - counts = orchestrate.orchestrate(test1, test2) - assert counts == (2, 2) - assert test_calls == expected - - @staticmethod - def test_failure(): - test_calls = [] - - def make_test(name): - syncpoints = [None] * 4 - - def test(): # pragma: NO COVER - test_calls.append(name) # pragma: SYNCPOINT - test_calls.append(name) - - if syncpoints: - syncpoints.pop() - else: - assert True is False - - return test - - test1 = make_test("A") - test2 = make_test("B") - - expected = [ - "A", - "A", - "B", - "B", - "A", - "B", - "A", - "B", - "A", - "B", - "B", - "A", - "B", - "A", - "A", - "B", - "B", - "A", - "B", - "A", - ] - - with pytest.raises(AssertionError): - orchestrate.orchestrate(test1, test2) - - assert test_calls == expected - - -def test__conductor(): - conductor = orchestrate._Conductor() - items = [] - - def run_in_test_thread(): - conductor.notify() - items.append("test1") - conductor.standby() - items.append("test2") - conductor.notify() - conductor.standby() - items.append("test3") - conductor.notify() - - assert not items - test_thread = threading.Thread(target=run_in_test_thread) - - test_thread.start() - conductor.wait() - assert items == ["test1"] - - conductor.go() - conductor.wait() - assert items == ["test1", "test2"] - - conductor.go() - conductor.wait() - assert items == ["test1", "test2", "test3"] - - -def test__get_syncpoints(): # pragma: SYNCPOINT test_get_syncpoints - with open(__file__, "r") as file: - lines = enumerate(file, start=1) - for expected_lineno, line in lines: # pragma: NO BRANCH COVER - if "# pragma: SYNCPOINT test_get_syncpoints" in line: - break - - orchestrate._get_syncpoints(__file__) - syncpoints = orchestrate._SYNCPOINTS[__file__]["test_get_syncpoints"] - assert syncpoints == {expected_lineno} - - -class Test_TestThread: - @staticmethod - def test__sync(): - test_thread = orchestrate._TestThread(None, None) - test_thread.conductor = mock.Mock() - test_thread._sync() - - test_thread.conductor.notify.assert_called_once_with() - test_thread.conductor.standby.assert_called_once_with() - - @staticmethod - def test__trace_no_source_file(): - orchestrate._SYNCPOINTS.clear() - frame = mock.Mock(f_globals={}, spec=("f_globals",)) - test_thread = orchestrate._TestThread(None, None) - assert test_thread._trace(frame, None, None) is None - assert not orchestrate._SYNCPOINTS - - @staticmethod - def test__trace_this_source_file(): - orchestrate._SYNCPOINTS.clear() - frame = mock.Mock( - f_globals={"__file__": __file__}, - f_lineno=1, - spec=( - "f_globals", - "f_lineno", - ), - ) - test_thread = orchestrate._TestThread(None, None) - assert test_thread._trace(frame, None, None) == test_thread._trace - assert __file__ in orchestrate._SYNCPOINTS - - @staticmethod - def test__trace_reach_syncpoint(): - with open(__file__, "r") as file: - lines = enumerate(file, start=1) - for syncpoint_lineno, line in lines: # pragma: NO BRANCH COVER - if "# pragma: SYNCPOINT test_get_syncpoints" in line: - break - - orchestrate._SYNCPOINTS.clear() - frame = mock.Mock( - f_globals={"__file__": __file__}, - f_lineno=syncpoint_lineno, - spec=( - "f_globals", - "f_lineno", - ), - ) - test_thread = orchestrate._TestThread(None, "test_get_syncpoints") - test_thread._sync = mock.Mock() - assert test_thread._trace(frame, None, None) == test_thread._trace - test_thread._sync.assert_not_called() - - frame = mock.Mock( - f_globals={"__file__": __file__}, - f_lineno=syncpoint_lineno + 1, - spec=( - "f_globals", - "f_lineno", - ), - ) - assert test_thread._trace(frame, None, None) == test_thread._trace - test_thread._sync.assert_called_once_with() - - @staticmethod - def test__trace_other_source_file_with_no_syncpoints(): - filename = orchestrate.__file__ - if filename.endswith(".pyc"): # pragma: NO COVER - filename = filename[:-1] - - orchestrate._SYNCPOINTS.clear() - frame = mock.Mock( - f_globals={"__file__": filename + "c"}, - f_lineno=1, - spec=( - "f_globals", - "f_lineno", - ), - ) - test_thread = orchestrate._TestThread(None, None) - assert test_thread._trace(frame, None, None) is None - syncpoints = orchestrate._SYNCPOINTS[filename] - assert not syncpoints diff --git a/tests/unit/test_prefixer.py b/tests/unit/test_prefixer.py deleted file mode 100644 index bfe2e8e..0000000 --- a/tests/unit/test_prefixer.py +++ /dev/null @@ -1,89 +0,0 @@ -# Copyright 2021 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. - -import datetime -import re - -import pytest # type: ignore - -import test_utils.prefixer - - -class FakeDateTime(object): - """Fake datetime class since pytest can't monkeypatch attributes of - built-in/extension type. - """ - - def __init__(self, fake_now): - self._fake_now = fake_now - - def now(self, timezone): - return self._fake_now - - strptime = datetime.datetime.strptime - - -@pytest.mark.parametrize( - ("repo", "relative_dir", "separator", "expected"), - [ - ( - "python-bigquery", - "samples/snippets", - "_", - "python_bigquery_samples_snippets", - ), - ("python-storage", "samples\\snippets", "-", "python-storage-samples-snippets"), - ], -) -def test_common_prefix(repo, relative_dir, separator, expected): - got = test_utils.prefixer._common_prefix(repo, relative_dir, separator=separator) - assert got == expected - - -def test_create_prefix(monkeypatch): - fake_datetime = FakeDateTime(datetime.datetime(2021, 6, 21, 3, 32, 0)) - monkeypatch.setattr(datetime, "datetime", fake_datetime) - - prefixer = test_utils.prefixer.Prefixer( - "python-test-utils", "tests/unit", separator="?" - ) - got = prefixer.create_prefix() - parts = got.split("?") - assert len(parts) == 7 - assert "?".join(parts[:5]) == "python?test?utils?tests?unit" - datetime_part = parts[5] - assert datetime_part == "20210621033200" - random_hex_part = parts[6] - assert re.fullmatch("[0-9a-f]+", random_hex_part) - - -@pytest.mark.parametrize( - ("resource_name", "separator", "expected"), - [ - ("test_utils_created_elsewhere", "_", False), - ("test_utils_20210620120000", "_", False), - ("test_utils_20210620120000_abcdef_my_name", "_", False), - ("test_utils_20210619120000", "_", True), - ("test_utils_20210619120000_abcdef_my_name", "_", True), - ("test?utils?created?elsewhere", "_", False), - ("test?utils?20210620120000", "?", False), - ("test?utils?20210619120000", "?", True), - ], -) -def test_should_cleanup(resource_name, separator, expected, monkeypatch): - fake_datetime = FakeDateTime(datetime.datetime(2021, 6, 21, 3, 32, 0)) - monkeypatch.setattr(datetime, "datetime", fake_datetime) - - prefixer = test_utils.prefixer.Prefixer("test", "utils", separator=separator) - assert prefixer.should_cleanup(resource_name) == expected