diff --git a/b2sdk/_internal/testing/fixtures/buckets.py b/b2sdk/_internal/testing/fixtures/buckets.py index 65da565b4..cdfd50683 100644 --- a/b2sdk/_internal/testing/fixtures/buckets.py +++ b/b2sdk/_internal/testing/fixtures/buckets.py @@ -35,13 +35,13 @@ def dont_cleanup_old_buckets(request): @pytest.fixture(scope='session') -def bucket_name_prefix(): - return get_bucket_name_prefix(8) +def general_bucket_name_prefix(): + return GENERAL_BUCKET_NAME_PREFIX @pytest.fixture(scope='session') -def general_bucket_name_prefix(): - return GENERAL_BUCKET_NAME_PREFIX +def bucket_name_prefix(general_bucket_name_prefix): + return get_bucket_name_prefix(8, general_bucket_name_prefix) @pytest.fixture(scope='session') diff --git a/b2sdk/_internal/testing/helpers/bucket_manager.py b/b2sdk/_internal/testing/helpers/bucket_manager.py index 2fbbb929c..36611b3f1 100644 --- a/b2sdk/_internal/testing/helpers/bucket_manager.py +++ b/b2sdk/_internal/testing/helpers/bucket_manager.py @@ -10,7 +10,6 @@ from __future__ import annotations import logging -import platform from collections.abc import Iterable from datetime import datetime, timedelta from itertools import chain @@ -26,11 +25,11 @@ BUCKET_CREATED_AT_MILLIS, BUCKET_NAME_LENGTH, GENERAL_BUCKET_NAME_PREFIX, - random_token, + NODE_DESCRIPTION, + bucket_name_part, ) from b2sdk._internal.utils import current_time_millis -NODE_DESCRIPTION = f'{platform.node()}: {platform.platform()}' ONE_HOUR_MILLIS = 60 * 60 * 1000 BUCKET_CLEANUP_PERIOD_MILLIS = timedelta(hours=3).total_seconds() * 1000 @@ -52,7 +51,7 @@ def __init__( self.bucket_name_log: list[str] = [] def new_bucket_name(self) -> str: - bucket_name = self.current_run_prefix + random_token( + bucket_name = self.current_run_prefix + bucket_name_part( BUCKET_NAME_LENGTH - len(self.current_run_prefix) ) self.bucket_name_log.append(bucket_name) diff --git a/b2sdk/_internal/testing/helpers/buckets.py b/b2sdk/_internal/testing/helpers/buckets.py index a20e1f5f4..bc9df2dc9 100644 --- a/b2sdk/_internal/testing/helpers/buckets.py +++ b/b2sdk/_internal/testing/helpers/buckets.py @@ -9,20 +9,62 @@ ###################################################################### from __future__ import annotations +import logging +import os +import platform +import random import secrets +import time +from hashlib import sha256 from b2sdk._internal.http_constants import BUCKET_NAME_CHARS_UNIQ, BUCKET_NAME_LENGTH_RANGE +logger = logging.getLogger(__name__) + GENERAL_BUCKET_NAME_PREFIX = 'sdktst' BUCKET_NAME_LENGTH = BUCKET_NAME_LENGTH_RANGE[1] BUCKET_CREATED_AT_MILLIS = 'created_at_millis' +NODE_DESCRIPTION = f'{platform.node()}: {platform.platform()} {platform.python_version()}' + + +def get_seed() -> str: + """ + Get seed for random number generator. + + The `WORKFLOW_ID` variable has to be set in the CI to uniquely identify + the current workflow (including the attempt) + """ + seed = ''.join( + ( + os.getenv('WORKFLOW_ID', secrets.token_hex(8)), + NODE_DESCRIPTION, + str(time.time_ns()), + os.getenv('PYTEST_XDIST_WORKER', 'gw0'), + ) + ) + return sha256(seed.encode()).hexdigest()[:16] -RNG = secrets.SystemRandom() + +RNG_SEED = get_seed() +RNG = random.Random(RNG_SEED) +RNG_COUNTER = 0 def random_token(length: int, chars: str = BUCKET_NAME_CHARS_UNIQ) -> str: return ''.join(RNG.choice(chars) for _ in range(length)) -def get_bucket_name_prefix(rnd_len: int = 8) -> str: - return GENERAL_BUCKET_NAME_PREFIX + random_token(rnd_len) +def bucket_name_part(length: int) -> str: + assert length >= 1 + global RNG_COUNTER + RNG_COUNTER += 1 + name_part = random_token(length, BUCKET_NAME_CHARS_UNIQ) + logger.info('RNG_SEED: %s', RNG_SEED) + logger.info('RNG_COUNTER: %i, length: %i', RNG_COUNTER, length) + logger.info('name_part: %s', name_part) + logger.info('WORKFLOW_ID: %s', os.getenv('WORKFLOW_ID')) + return name_part + + +def get_bucket_name_prefix(rnd_len: int = 8, prefix: str = GENERAL_BUCKET_NAME_PREFIX) -> str: + return prefix + bucket_name_part(rnd_len) diff --git a/b2sdk/v3/testing/__init__.py b/b2sdk/v3/testing/__init__.py index b0ae2d389..2cdbd76ed 100644 --- a/b2sdk/v3/testing/__init__.py +++ b/b2sdk/v3/testing/__init__.py @@ -17,12 +17,14 @@ GENERAL_BUCKET_NAME_PREFIX, BUCKET_NAME_LENGTH, BUCKET_CREATED_AT_MILLIS, + NODE_DESCRIPTION, RNG, - random_token, + RNG_COUNTER, + RNG_SEED, get_bucket_name_prefix, + random_token, ) from b2sdk._internal.testing.helpers.bucket_manager import ( - NODE_DESCRIPTION, ONE_HOUR_MILLIS, BUCKET_CLEANUP_PERIOD_MILLIS, BucketManager, diff --git a/changelog.d/+tests-seed-generation.changed.md b/changelog.d/+tests-seed-generation.changed.md new file mode 100644 index 000000000..12e2dcd4d --- /dev/null +++ b/changelog.d/+tests-seed-generation.changed.md @@ -0,0 +1 @@ +Improve seed generation for containers' names in tests. \ No newline at end of file diff --git a/test/integration/conftest.py b/test/integration/conftest.py index ab2b6d380..90cfcf141 100644 --- a/test/integration/conftest.py +++ b/test/integration/conftest.py @@ -9,4 +9,4 @@ ###################################################################### -pytest_plugins = ["b2sdk.v3.testing"] +pytest_plugins = ['b2sdk.v3.testing']