diff --git a/install.py b/install.py index 5688ca014..f6b5a5419 100644 --- a/install.py +++ b/install.py @@ -5608,7 +5608,13 @@ def _print_plan( print(line) else: largest = sorted(by_system, key=lambda s: (-_bytes(by_system[s]), s)) - print(f" {_plural(len(largest), 'system')}, the largest first:") + # A file several systems share is a group of its own: "88 systems" + # were 66 systems, as the full screen counts them, and 22 groups. + systems = {name for entry in to_download for name in entry_systems(entry)} + groups = (f"{_plural(len(systems), 'system')} in {len(largest)} groups" + if systems and len(systems) != len(largest) + else _plural(len(largest), 'system')) + print(f" {groups}, the largest first:") for system in largest[:PLAN_LARGEST_SYSTEMS]: print(f" {term.bold(system)} {_sized(by_system[system])}") if len(largest) > PLAN_LARGEST_SYSTEMS: @@ -6486,7 +6492,7 @@ INSTALLED_ONLY = "installed" def _prompt_custom_selection( plat: str, files: list[dict], omitted: list[dict], installed: tuple[list[dict], list[dict]] | None = None, - name: str = "", + name: str = "", dry_run: bool = False, ) -> tuple[list[str], list[str], list[str]] | str | None: """Offer to narrow the install by system, core and region. @@ -6496,7 +6502,9 @@ def _prompt_custom_selection( everything on its axis. """ print(f"\n{name or display_name(plat)}: {_sized(files)}.") - print(" Enter) install everything") + # A dry run installs nothing: "install everything" was asked before it. + print(" Enter) list everything, nothing is written" if dry_run + else " Enter) install everything") if installed is not None: print(f" i) only what the installed cores read: {_sized(installed[0])}") print(" c) choose systems, cores and regions") @@ -9610,7 +9618,7 @@ def _install(args: argparse.Namespace, real_stdout: TextIO) -> None: ) selection = _prompt_custom_selection( plat_name, files, omitted_files, installed, - display_name(plat_name, manifest), + display_name(plat_name, manifest), args.dry_run, ) if selection is None: smaller = (" Nothing downloaded. To install less, run the same command " diff --git a/tests/test_install_experience.py b/tests/test_install_experience.py index 16c384161..9b858c365 100644 --- a/tests/test_install_experience.py +++ b/tests/test_install_experience.py @@ -1191,6 +1191,33 @@ class OfflineWithNothingFound(unittest.TestCase): self.assertIn("Use --platform and --dest", said) +class TheDryRunWords(unittest.TestCase): + """The FAQ's dry run asked "Enter) install everything" before a run + that installs nothing, then counted 88 systems where the full screen + lists 66: a shared file made a group of its own.""" + + def test_the_question_says_nothing_is_written(self): + out = io.StringIO() + files = [{"dest": "a.bin", "size": 1, "systems": ["x"]}] + with mock.patch.object(install, "_ask", return_value=""), \ + contextlib.redirect_stdout(out): + install._prompt_custom_selection("retroarch", files, [], None, "RetroArch", + dry_run=True) + self.assertNotIn("install everything", out.getvalue()) + self.assertIn("nothing is written", out.getvalue()) + + def test_the_largest_systems_are_counted_as_systems(self): + files = [{"dest": f"{n}.bin", "size": n + 1, "systems": [f"s{n}"]} + for n in range(12)] + files.append({"dest": "shared.bin", "size": 1, "systems": ["s0", "s1"]}) + out = io.StringIO() + with mock.patch.object(install.shutil, "get_terminal_size", + return_value=os.terminal_size((80, 10))), \ + contextlib.redirect_stdout(out): + install._print_plan(files, Path("dest"), install.Terminal(io.StringIO())) + self.assertIn("12 systems in 13 groups, the largest first", out.getvalue()) + + def _source_of(function) -> str: import inspect