mirror of https://github.com/grpc/grpc.git
[PSM Interop] Add unittests CI with github actions (#34125)
- Add Github Action to conditionally run PSM Interop unit tests: - Only run when changes are detected in `tools/run_tests/xds_k8s_test_driver` or any of the proto files used by the driver - Only run against PRs and pushes to `master`, `v1.*.*` branches - Runs using `python3.9` and `python3.10` - Ready to be added to the list of required GitHub checks - Add `tools/run_tests/xds_k8s_test_driver/tests/unit/__main__.py` test loader that recursively discovers all unit tests in `tools/run_tests/xds_k8s_test_driver/tests/unit` - Add basic coverage for `XdsTestClient` and `XdsTestServer` to verify the test loader picks up all folders Related: - First unit tests without automated CI added in #34097pull/34130/head^2
parent
440eef2288
commit
1708f631ee
6 changed files with 261 additions and 2 deletions
@ -0,0 +1,74 @@ |
||||
name: PSM Interop |
||||
|
||||
on: |
||||
pull_request: |
||||
push: |
||||
branches: |
||||
- master |
||||
- 'v1.*' |
||||
|
||||
permissions: |
||||
contents: read |
||||
|
||||
jobs: |
||||
unittest: |
||||
# By default, only version is printed out in parens, e.g. "unittest (3.10)" |
||||
# This changes it to "unittest (python3.10)" |
||||
name: "unittest (python${{ matrix.python_version }})" |
||||
runs-on: ubuntu-latest |
||||
strategy: |
||||
matrix: |
||||
python_version: ["3.9", "3.10"] |
||||
fail-fast: false |
||||
permissions: |
||||
pull-requests: read # Used by paths-filter to read the diff. |
||||
defaults: |
||||
run: |
||||
working-directory: 'tools/run_tests/xds_k8s_test_driver' |
||||
|
||||
steps: |
||||
- uses: actions/checkout@v3 |
||||
|
||||
# To add this job to required GitHub checks, it's not enough to use |
||||
# the on.pull_request.paths filter. For required checks, the job needs to |
||||
# return the success status, and not be skipped. |
||||
# Using paths-filter action, we skip the setup/test steps when psm interop |
||||
# files are unchanged, and the job returns success. |
||||
- uses: dorny/paths-filter@v2 |
||||
id: paths_filter |
||||
with: |
||||
filters: | |
||||
psm_interop_src: |
||||
- 'tools/run_tests/xds_k8s_test_driver/**' |
||||
- 'src/proto/grpc/testing/empty.proto' |
||||
- 'src/proto/grpc/testing/messages.proto' |
||||
- 'src/proto/grpc/testing/test.proto' |
||||
|
||||
- uses: actions/setup-python@v4 |
||||
if: ${{ steps.paths_filter.outputs.psm_interop_src == 'true' }} |
||||
with: |
||||
python-version: "${{ matrix.python_version }}" |
||||
cache: 'pip' |
||||
cache-dependency-path: 'tools/run_tests/xds_k8s_test_driver/requirements.lock' |
||||
|
||||
- name: "Install requirements" |
||||
if: ${{ steps.paths_filter.outputs.psm_interop_src == 'true' }} |
||||
run: | |
||||
pip list |
||||
pip install --upgrade pip setuptools |
||||
pip list |
||||
pip install -r requirements.lock |
||||
pip list |
||||
|
||||
- name: "Generate protos" |
||||
if: ${{ steps.paths_filter.outputs.psm_interop_src == 'true' }} |
||||
run: > |
||||
python -m grpc_tools.protoc --proto_path=../../../ |
||||
--python_out=. --grpc_python_out=. |
||||
src/proto/grpc/testing/empty.proto |
||||
src/proto/grpc/testing/messages.proto |
||||
src/proto/grpc/testing/test.proto |
||||
|
||||
- name: "Run unit tests" |
||||
if: ${{ steps.paths_filter.outputs.psm_interop_src == 'true' }} |
||||
run: python -m tests.unit |
@ -0,0 +1,27 @@ |
||||
# Copyright 2023 The gRPC Authors |
||||
# |
||||
# 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. |
||||
"""Discover and run all unit tests recursively.""" |
||||
|
||||
import pathlib |
||||
|
||||
from absl.testing import absltest |
||||
|
||||
|
||||
def load_tests(loader: absltest.TestLoader, unused_tests, unused_pattern): |
||||
unit_tests_root = pathlib.Path(__file__).parent |
||||
return loader.discover(f"{unit_tests_root}", pattern="*_test.py") |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
absltest.main() |
@ -0,0 +1,13 @@ |
||||
# Copyright 2023 gRPC authors. |
||||
# |
||||
# 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. |
@ -0,0 +1,55 @@ |
||||
# Copyright 2023 gRPC authors. |
||||
# |
||||
# 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 typing import Optional |
||||
|
||||
from absl.testing import absltest |
||||
|
||||
from framework.test_app import client_app |
||||
|
||||
# Alias |
||||
XdsTestClient = client_app.XdsTestClient |
||||
|
||||
# Test values. |
||||
CANNED_IP: str = "10.0.0.42" |
||||
CANNED_RPC_PORT: int = 1111 |
||||
CANNED_HOSTNAME: str = "test-client.local" |
||||
CANNED_SERVER_TARGET: str = "xds:///test-server" |
||||
|
||||
|
||||
class ClientAppTest(absltest.TestCase): |
||||
"""Unit test for the ClientApp.""" |
||||
|
||||
def test_constructor(self): |
||||
xds_client = XdsTestClient( |
||||
ip=CANNED_IP, |
||||
rpc_port=CANNED_RPC_PORT, |
||||
hostname=CANNED_HOSTNAME, |
||||
server_target=CANNED_SERVER_TARGET, |
||||
) |
||||
# Channels list empty. |
||||
self.assertEmpty(xds_client.channels) |
||||
|
||||
# Test fields set as is. |
||||
self.assertEqual(xds_client.ip, CANNED_IP) |
||||
self.assertEqual(xds_client.rpc_port, CANNED_RPC_PORT) |
||||
self.assertEqual(xds_client.server_target, CANNED_SERVER_TARGET) |
||||
self.assertEqual(xds_client.hostname, CANNED_HOSTNAME) |
||||
|
||||
# Test optional argument defaults. |
||||
self.assertEqual(xds_client.rpc_host, CANNED_IP) |
||||
self.assertEqual(xds_client.maintenance_port, CANNED_RPC_PORT) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
absltest.main() |
@ -0,0 +1,84 @@ |
||||
# Copyright 2023 gRPC authors. |
||||
# |
||||
# 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 typing import Optional |
||||
|
||||
from absl.testing import absltest |
||||
|
||||
from framework.test_app import server_app |
||||
|
||||
# Alias |
||||
XdsTestServer = server_app.XdsTestServer |
||||
|
||||
# Test values. |
||||
CANNED_IP: str = "10.0.0.43" |
||||
CANNED_RPC_PORT: int = 2222 |
||||
CANNED_HOSTNAME: str = "test-server.local" |
||||
CANNED_XDS_HOST: str = "xds-test-server" |
||||
CANNED_XDS_PORT: int = 42 |
||||
|
||||
|
||||
class ServerAppTest(absltest.TestCase): |
||||
"""Unit test for the XdsTestServer.""" |
||||
|
||||
def test_constructor(self): |
||||
xds_server = XdsTestServer( |
||||
ip=CANNED_IP, |
||||
rpc_port=CANNED_RPC_PORT, |
||||
hostname=CANNED_HOSTNAME, |
||||
) |
||||
# Channels list empty. |
||||
self.assertEmpty(xds_server.channels) |
||||
|
||||
# Test fields set as is. |
||||
self.assertEqual(xds_server.ip, CANNED_IP) |
||||
self.assertEqual(xds_server.rpc_port, CANNED_RPC_PORT) |
||||
self.assertEqual(xds_server.hostname, CANNED_HOSTNAME) |
||||
|
||||
# Test optional argument defaults. |
||||
self.assertEqual(xds_server.rpc_host, CANNED_IP) |
||||
self.assertEqual(xds_server.maintenance_port, CANNED_RPC_PORT) |
||||
self.assertEqual(xds_server.secure_mode, False) |
||||
|
||||
def test_xds_address(self): |
||||
"""Verifies the behavior of set_xds_address(), xds_address, xds_uri.""" |
||||
xds_server = XdsTestServer( |
||||
ip=CANNED_IP, |
||||
rpc_port=CANNED_RPC_PORT, |
||||
hostname=CANNED_HOSTNAME, |
||||
) |
||||
self.assertEqual(xds_server.xds_uri, "", msg="Must be empty when unset") |
||||
|
||||
xds_server.set_xds_address(CANNED_XDS_HOST, CANNED_XDS_PORT) |
||||
self.assertEqual(xds_server.xds_uri, "xds:///xds-test-server:42") |
||||
|
||||
xds_server.set_xds_address(CANNED_XDS_HOST, None) |
||||
self.assertEqual( |
||||
xds_server.xds_uri, |
||||
"xds:///xds-test-server", |
||||
msg="Must not contain ':port' when the port is not set", |
||||
) |
||||
|
||||
xds_server.set_xds_address(None, None) |
||||
self.assertEqual(xds_server.xds_uri, "", msg="Must be empty when reset") |
||||
|
||||
xds_server.set_xds_address(None, CANNED_XDS_PORT) |
||||
self.assertEqual( |
||||
xds_server.xds_uri, |
||||
"", |
||||
msg="Must be empty when only port is set", |
||||
) |
||||
|
||||
|
||||
if __name__ == "__main__": |
||||
absltest.main() |
Loading…
Reference in new issue