Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ dependencies = [
"pyyaml",
"schema",
"rich",
"randomname",
"typing_extensions; python_version < '3.11'",
]

Expand Down Expand Up @@ -141,6 +142,10 @@ exclude = [
"src/canary_gitlab/tests/*",
]

[[tool.mypy.overrides]]
module = "randomname.*"
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = "requests.*"
ignore_missing_imports = true
Expand Down
2 changes: 1 addition & 1 deletion src/_canary/plugins/subcommands/selection.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def execute(self, args: "argparse.Namespace") -> int:
raise ValueError(
logging.colorize(
f"Selection {args.tag!r} already exists, run "
f"[bold]canary refresh {args.tag}[/] to regnerate specs"
f"[bold]canary selection refresh {args.tag}[/] to regnerate specs"
)
)
if not args.scanpaths:
Expand Down
34 changes: 34 additions & 0 deletions src/_canary/util/names.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# Copyright NTESS. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: MIT
from typing import Any
from typing import Iterable

import randomname

default_groups = ["a/colors", "n/apex_predators"]


def random_name(groups: Any = default_groups, seed: int | None = None) -> str:
"""Generate a random name with one random entry from each of the provided groups"""
return randomname.generate(*groups, seed=seed)


def unique_random_name(
existing_names: Iterable[str],
max_samples: int = 20,
groups: Any = default_groups,
seed: int | None = None,
) -> str:
"""Attempt to generate a random name that is not in `existing_names` within `max_samples`.

Raises `ValueError` if unable to generate a unique name
"""
for _ in range(max_samples):
name = random_name(groups=groups, seed=seed)
if name not in existing_names:
return name
else:
raise ValueError(
f"unable to generate name outside {existing_names} in {max_samples} attempts"
)
4 changes: 3 additions & 1 deletion src/_canary/workspace.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from .util.filesystem import write_directory_tag
from .util.graph import TopologicalSorter
from .util.graph import static_order
from .util.names import unique_random_name
from .version import __static_version__

logger = logging.get_logger(__name__)
Expand Down Expand Up @@ -403,7 +404,7 @@ def create_selection(
regex: str | None = None,
) -> list["ResolvedSpec"]:
"""Find test case generators in scan_paths and add them to this workspace"""
tag = tag or datetime.datetime.now().isoformat(timespec="microseconds")
tag = tag or unique_random_name(self.db.tags)
collector = Collector()
collector.add_scanpaths(scanpaths)
generators = collector.run()
Expand All @@ -429,6 +430,7 @@ def create_selection(
owners=owners,
regex=regex,
)
logger.info(f"Created selection '[bold]{tag}[/]'")
return specs

def apply_selection_rules(
Expand Down
44 changes: 44 additions & 0 deletions tests/util/names_tests.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Copyright NTESS. See COPYRIGHT file for details.
#
# SPDX-License-Identifier: MIT
from _canary.util import names


def test_random_name_with_seed_is_repeatable():
seed = 1
assert len({names.random_name(seed=seed) for _ in range(10)}) == 1


def test_random_name_does_not_repeat_for_small_count():
N = 30
# set the random number generator seed so this test is repeatable
names.random_name(seed=1)
random_names = {names.random_name() for _ in range(N)}
print(random_names)
assert len(random_names) == N


def test_unique_random_name_raises_if_unable_to_generate_name():
seed = 1
existing_names = {names.random_name(seed=seed)}
try:
_ = names.unique_random_name(existing_names, seed=seed)
assert False
except ValueError as e:
print(e)
assert True
except:
assert False


def test_unique_random_name():
seed = 1
N = 20
names.random_name(seed=seed)
existing_names = {names.random_name() for _ in range(N)}

# reset the seed
names.random_name(seed=seed)
unique_name = names.unique_random_name(existing_names=existing_names, max_samples=N + 1)

assert unique_name not in existing_names
Loading