Skip to content

Commit e768973

Browse files
authored
Run tests concurrently in CI (#845)
* Run tests concurrently in CI * add _get_bucket_name
1 parent 991faba commit e768973

8 files changed

Lines changed: 53 additions & 34 deletions

File tree

.github/workflows/ci.yml

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ jobs:
2424
fail-fast: false
2525
matrix:
2626
python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"]
27+
env:
28+
STORAGE_EMULATOR_HOST: http://localhost:4443
2729

2830
steps:
2931
- name: Checkout source
@@ -46,36 +48,44 @@ jobs:
4648
- name: install
4749
run: |
4850
pip install -e .
51+
52+
- name: Start GCS Emulator
53+
run: |
54+
docker run -d -p 4443:4443 --name gcsfs_test fsouza/fake-gcs-server:latest -scheme http -public-host 0.0.0.0:4443 -external-url http://localhost:4443 -backend memory
55+
timeout 30s bash -c 'until curl -s http://localhost:4443/storage/v1/b; do sleep 1; done'
56+
4957
- name: Run Standard Bucket Tests with extended feature support turned OFF
5058
run: |
51-
export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/gcsfs/tests/fake-secret.json
59+
export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/gcsfs/tests/fake-service-account-credentials.json
5260
export GCSFS_EXPERIMENTAL_ZB_HNS_SUPPORT="false"
5361
pytest -vv -s \
5462
--cov=gcsfs --cov-report=xml \
5563
--log-format="%(asctime)s %(levelname)s %(message)s" \
5664
--log-date-format="%H:%M:%S" \
65+
-n auto \
5766
gcsfs/
5867
- name: Run Benchmark Infrastructure Unit Tests
5968
run: |
6069
pip install -r gcsfs/tests/perf/microbenchmarks/requirements.txt
6170
pytest gcsfs/tests/perf/microbenchmarks --run-benchmarks-infra
6271
- name: Run Standard Bucket Tests with default ON extended feature support
6372
run: |
64-
export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/gcsfs/tests/fake-secret.json
73+
export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/gcsfs/tests/fake-service-account-credentials.json
6574
pytest -vv -s \
6675
--cov=gcsfs --cov-append --cov-report=xml \
6776
--log-format="%(asctime)s %(levelname)s %(message)s" \
6877
--log-date-format="%H:%M:%S" \
78+
-n auto \
6979
gcsfs/
7080
- name: Run Extended tests (Zonal & HNS Enabled)
7181
run: |
72-
export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/gcsfs/tests/fake-secret.json
73-
# TODO: Use dedicated test variables to decouple specialised test execution from the GCSFS_EXPERIMENTAL_ZB_HNS_SUPPORT feature flag.
82+
export GOOGLE_APPLICATION_CREDENTIALS=$(pwd)/gcsfs/tests/fake-service-account-credentials.json
7483
export GCSFS_EXPERIMENTAL_ZB_HNS_SUPPORT="true"
7584
pytest -vv -s \
7685
--cov=gcsfs --cov-append --cov-report=xml \
7786
--log-format="%(asctime)s %(levelname)s %(message)s" \
7887
--log-date-format="%H:%M:%S" \
88+
-n auto \
7989
gcsfs/tests/test_extended_gcsfs.py \
8090
gcsfs/tests/test_extended_hns_gcsfs.py \
8191
gcsfs/tests/test_extended_gcsfs_unit.py \

environment_gcsfs.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ dependencies:
2222
- pytest-asyncio
2323
- pytest-cov
2424
- pytest-subtests
25+
- pytest-xdist
2526
- requests
2627
- ujson
2728
- pip:

gcsfs/tests/fake-secret.json

Lines changed: 0 additions & 8 deletions
This file was deleted.

gcsfs/tests/settings.py

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
import os
22

3-
TEST_BUCKET = os.getenv("GCSFS_TEST_BUCKET", "gcsfs_test")
4-
TEST_VERSIONED_BUCKET = os.getenv("GCSFS_TEST_VERSIONED_BUCKET", "gcsfs_test_versioned")
5-
TEST_HNS_BUCKET = os.getenv("GCSFS_HNS_TEST_BUCKET", "gcsfs_hns_test")
6-
TEST_ZONAL_BUCKET = os.getenv("GCSFS_ZONAL_TEST_BUCKET", "gcsfs_zonal_test")
3+
4+
def _get_bucket_name(env_var: str, default_name: str) -> str:
5+
worker_id = os.environ.get("PYTEST_XDIST_WORKER")
6+
suffix = f"_{worker_id}" if worker_id else ""
7+
return os.getenv(env_var, default_name) + suffix
8+
9+
10+
TEST_BUCKET = _get_bucket_name("GCSFS_TEST_BUCKET", "gcsfs_test")
11+
TEST_VERSIONED_BUCKET = _get_bucket_name(
12+
"GCSFS_TEST_VERSIONED_BUCKET", "gcsfs_test_versioned"
13+
)
14+
TEST_HNS_BUCKET = _get_bucket_name("GCSFS_HNS_TEST_BUCKET", "gcsfs_hns_test")
15+
TEST_ZONAL_BUCKET = _get_bucket_name("GCSFS_ZONAL_TEST_BUCKET", "gcsfs_zonal_test")
716
TEST_PROJECT = os.getenv("GCSFS_TEST_PROJECT", "project")
8-
TEST_REQUESTER_PAYS_BUCKET = os.getenv(
17+
TEST_REQUESTER_PAYS_BUCKET = _get_bucket_name(
918
"GCSFS_TEST_REQ_PAYS_BUCKET", "gcsfs_test_req_pays"
1019
)
11-
TEST_HNS_REQUESTER_PAYS_BUCKET = os.getenv(
20+
TEST_HNS_REQUESTER_PAYS_BUCKET = _get_bucket_name(
1221
"GCSFS_HNS_TEST_REQ_PAYS_BUCKET", "gcsfs_hns_test_req_pays"
1322
)
1423
TEST_KMS_KEY = os.getenv(

gcsfs/tests/test_core.py

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2399,23 +2399,17 @@ def test_find_maxdepth(gcs):
23992399

24002400

24012401
def test_sign(gcs, monkeypatch):
2402+
if not gcs.on_google:
2403+
pytest.skip("Emulator does not support signing")
2404+
24022405
file = TEST_BUCKET + "/test.jpg"
24032406
with gcs.open(file, "wb") as f:
24042407
f.write(b"This is a test string")
24052408
assert gcs.cat(file) == b"This is a test string"
24062409

2407-
# `sign` is creating a google Client on its own, it needs a realistically
2408-
# looking credentials file.
2409-
if not gcs.on_google:
2410-
monkeypatch.setenv(
2411-
"GOOGLE_APPLICATION_CREDENTIALS",
2412-
os.path.dirname(__file__) + "/fake-service-account-credentials.json",
2413-
)
2414-
24152410
current_ts_utc = int(datetime.now(tz=timezone.utc).timestamp())
24162411
result = gcs.sign(file)
24172412

2418-
# Check it here since emulator doesn't really validate those values
24192413
params = parse_qs(urlparse(result).query)
24202414
assert int(params["Expires"][0]) >= current_ts_utc + 100
24212415

gcsfs/tests/test_credentials.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,10 @@ def test_connect_cloud_not_on_google():
8585
@pytest.mark.parametrize("token", ["", "incorrect.token", "x" * 100])
8686
def test_credentials_from_raw_token(token):
8787
with patch.dict(os.environ, {"FETCH_RAW_TOKEN_EXPIRY": "false"}):
88+
fs = GCSFileSystem(project="myproject", token=token)
89+
if not fs.on_google:
90+
pytest.skip("Emulator does not validate tokens")
8891
with pytest.raises(HttpError, match="Invalid Credentials"):
89-
fs = GCSFileSystem(project="myproject", token=token)
9092
fs.ls("/")
9193

9294

gcsfs/tests/test_extended_gcsfs.py

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1105,17 +1105,27 @@ def test_get_directory_from_zonal_bucket(extended_gcsfs):
11051105

11061106
@pytest.mark.asyncio
11071107
@pytest.mark.parametrize(
1108-
"source_bucket, dest_bucket, should_fail",
1108+
"source_bucket_type, dest_bucket_type, should_fail",
11091109
[
1110-
(TEST_ZONAL_BUCKET, TEST_ZONAL_BUCKET, True),
1111-
(TEST_ZONAL_BUCKET, TEST_BUCKET, True),
1112-
(TEST_BUCKET, TEST_ZONAL_BUCKET, True),
1113-
(TEST_BUCKET, TEST_BUCKET, False),
1110+
(BucketType.ZONAL_HIERARCHICAL, BucketType.ZONAL_HIERARCHICAL, True),
1111+
(BucketType.ZONAL_HIERARCHICAL, BucketType.NON_HIERARCHICAL, True),
1112+
(BucketType.NON_HIERARCHICAL, BucketType.ZONAL_HIERARCHICAL, True),
1113+
(BucketType.NON_HIERARCHICAL, BucketType.NON_HIERARCHICAL, False),
11141114
],
11151115
)
11161116
async def test_cp_file_not_implemented_error(
1117-
async_gcs, source_bucket, dest_bucket, should_fail
1117+
async_gcs, source_bucket_type, dest_bucket_type, should_fail
11181118
):
1119+
source_bucket = (
1120+
TEST_ZONAL_BUCKET
1121+
if source_bucket_type == BucketType.ZONAL_HIERARCHICAL
1122+
else TEST_BUCKET
1123+
)
1124+
dest_bucket = (
1125+
TEST_ZONAL_BUCKET
1126+
if dest_bucket_type == BucketType.ZONAL_HIERARCHICAL
1127+
else TEST_BUCKET
1128+
)
11191129
"""
11201130
Tests _cp_file behavior for combinations of Zonal and Standard buckets.
11211131
"""

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ dev = [
4545
"pytest-timeout",
4646
"pytest-cov",
4747
"pytest-subtests",
48+
"pytest-xdist",
4849
]
4950

5051
[project.urls]

0 commit comments

Comments
 (0)