|
24 | 24 | import tomllib |
25 | 25 | import types |
26 | 26 | import unittest |
27 | | -from collections.abc import Iterable |
| 27 | +from collections.abc import Iterable, Mapping |
28 | 28 | from pathlib import Path, PurePosixPath |
29 | | -from typing import Any, Protocol |
| 29 | +from typing import Any, ClassVar, Protocol |
30 | 30 |
|
31 | 31 | ROOT = Path(__file__).resolve().parent.parent |
32 | 32 | sys.path.insert(0, str(ROOT)) |
@@ -426,6 +426,57 @@ def test_a_running_part_is_not_held(self) -> None: |
426 | 426 | """ |
427 | 427 |
|
428 | 428 |
|
| 429 | +def buildable_variants(package: Any = None, variants: Any = None) -> list[str]: |
| 430 | + """Every variant that can be constructed on this machine, not merely named. |
| 431 | +
|
| 432 | + A member whose parts each run a file the repository cannot carry may hold |
| 433 | + some of those files and not others, so whether a part can be built is a fact |
| 434 | + about the variant rather than about the member. Reading it off the member's |
| 435 | + own `available` keeps that in one place; a member that publishes no such |
| 436 | + mapping can build everything it names. |
| 437 | + """ |
| 438 | + known = VARIANTS if variants is None else variants |
| 439 | + asked = getattr(PACKAGE if package is None else package, "available", None) |
| 440 | + if not callable(asked): |
| 441 | + return sorted(known) |
| 442 | + held = asked() |
| 443 | + if not isinstance(held, Mapping): |
| 444 | + return sorted(known) |
| 445 | + return sorted(name for name in known if name in held) |
| 446 | + |
| 447 | + |
| 448 | +class BuildableVariantTest(unittest.TestCase): |
| 449 | + """That a part nobody supplied a file for is not mistaken for a broken one. |
| 450 | +
|
| 451 | + Driven against a catalogue written here rather than the member's own, so the |
| 452 | + three answers are the same in every repository that carries this file. |
| 453 | + """ |
| 454 | + |
| 455 | + NAMED: ClassVar[dict[str, object]] = {"one": object(), "two": object()} |
| 456 | + |
| 457 | + class Silent: |
| 458 | + pass |
| 459 | + |
| 460 | + class Odd: |
| 461 | + @staticmethod |
| 462 | + def available() -> int: |
| 463 | + return 7 |
| 464 | + |
| 465 | + class Partial: |
| 466 | + @staticmethod |
| 467 | + def available() -> dict[str, int]: |
| 468 | + return {"two": 1} |
| 469 | + |
| 470 | + def test_a_member_that_publishes_no_catalogue_can_build_everything_it_names(self) -> None: |
| 471 | + self.assertEqual(buildable_variants(self.Silent(), self.NAMED), ["one", "two"]) |
| 472 | + |
| 473 | + def test_and_so_can_one_whose_catalogue_is_not_a_mapping(self) -> None: |
| 474 | + self.assertEqual(buildable_variants(self.Odd(), self.NAMED), ["one", "two"]) |
| 475 | + |
| 476 | + def test_a_member_holding_one_file_can_build_that_one(self) -> None: |
| 477 | + self.assertEqual(buildable_variants(self.Partial(), self.NAMED), ["two"]) |
| 478 | + |
| 479 | + |
429 | 480 | class PublishedSurfaceTest(unittest.TestCase): |
430 | 481 | """That everything the standard names is importable from the package itself. |
431 | 482 |
|
@@ -474,7 +525,7 @@ def test_a_part_is_built_by_Chip_taking_the_model_first(self) -> None: # noqa: |
474 | 525 |
|
475 | 526 | @unittest.skipUnless(A_PART and BUILDABLE, "no part can be built here") # pragma: no cover |
476 | 527 | def test_and_it_takes_a_model_by_name(self) -> None: |
477 | | - for name in sorted(VARIANTS): |
| 528 | + for name in buildable_variants(): |
478 | 529 | self.assertEqual(PACKAGE.Chip(name).model, name, name) |
479 | 530 |
|
480 | 531 | @unittest.skipUnless(A_PART, "not a part in the sense this checks") |
|
0 commit comments