From 83b23385a779d75ccc478e32f20e54ea2a4f8566 Mon Sep 17 00:00:00 2001 From: agessaman Date: Sun, 29 Mar 2026 13:32:30 -0700 Subject: [PATCH] Refine path formatting and testing in MultitestCommand MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Updated path formatting logic in `MultitestCommand` to include a corner marker (┐) for shared paths, enhancing visual clarity. - Improved documentation in `config.ini.example` to reflect changes in path formatting behavior. - Expanded test cases to validate the new corner marker functionality and ensure accurate representation of unique paths with shared prefixes. --- config.ini.example | 2 +- modules/commands/multitest_command.py | 42 +++++++++++++++-------- tests/test_multitest_command.py | 48 ++++++++++++++++++++------- 3 files changed, 65 insertions(+), 27 deletions(-) diff --git a/config.ini.example b/config.ini.example index 995912c..b495366 100644 --- a/config.ini.example +++ b/config.ini.example @@ -974,7 +974,7 @@ enabled = false # Leave empty to use default format # Example: "Found {path_count} unique path(s) for @[{sender}]:\n{paths}" response_format = @[{sender}] found {path_count} unique path(s):\n{paths} -# When true, {paths} uses shared-prefix lines plus tree branches (├ U+251C / └ U+2514; continuations may use ├─ / └─ with U+2500) +# When true, {paths} uses shared-prefix lines ending with ┐ U+2510 when branching, then ├ U+251C / └ U+2514 (continuations may use ├─ / └─ with U+2500) condense_paths = false [Greeter_Command] diff --git a/modules/commands/multitest_command.py b/modules/commands/multitest_command.py index 064567d..ddf9ad4 100644 --- a/modules/commands/multitest_command.py +++ b/modules/commands/multitest_command.py @@ -21,6 +21,7 @@ _HORIZ = "\u2500" # ─ (BOX DRAWINGS LIGHT HORIZONTAL) # Second-level branches: ├─ / └─ (horizontal continues the tee) _BRANCH_CHILD_INTER = f"{_BRANCH_INTER}{_HORIZ} " _BRANCH_CHILD_LAST = f"{_BRANCH_LAST}{_HORIZ} " +_BRANCH_CORNER = "\u2510" # ┐ (marks end of common path before branches) def _tree_branch_lines_flat(suffixes: list[str]) -> list[str]: @@ -36,7 +37,11 @@ def _tree_branch_lines_flat(suffixes: list[str]) -> list[str]: def _grouped_suffix_line_specs(non_empty: list[list[str]]) -> list[tuple[str, str]]: - """Build (line_kind, text) rows: 'head' uses ├/└ + space; 'nest' uses ├─/└─ + text.""" + """Build (line_kind, text) rows: group by first hop, then longest in-group prefix on the head line. + + So paths 96,e0 / 96,e0,01 / … share head '96,e0' and nest 01, … instead of head '96' with + misleading 'e0' as if it were the only endpoint under that branch. + """ by_first: dict[str, list[list[str]]] = defaultdict(list) for suf in non_empty: by_first[suf[0]].append(suf[1:]) @@ -44,15 +49,19 @@ def _grouped_suffix_line_specs(non_empty: list[list[str]]) -> list[tuple[str, st specs: list[tuple[str, str]] = [] for ft in sorted(by_first.keys()): rests = by_first[ft] - if len(rests) == 1: - r = rests[0] - if not r: - specs.append(("head", ft)) - else: - specs.append(("head", ",".join([ft, *r]))) + full_sufs = [[ft, *r] for r in rests] + if len(full_sufs) == 1: + specs.append(("head", ",".join(full_sufs[0]))) continue - specs.append(("head", ft)) - for rem in sorted((r for r in rests if r), key=lambda x: ",".join(x)): + inner_lcp = _longest_common_prefix(full_sufs) + head_text = ",".join(inner_lcp) + remainders = [s[len(inner_lcp) :] for s in full_sufs] + nested = sorted((r for r in remainders if r), key=lambda x: ",".join(x)) + if not nested: + specs.append(("head", head_text)) + continue + specs.append(("head", head_text)) + for rem in nested: specs.append(("nest", ",".join(rem))) return specs @@ -73,7 +82,7 @@ def _apply_tee_prefixes(specs: list[tuple[str, str]]) -> list[str]: def _format_suffix_branch_lines(suffix_tokens: list[list[str]], short_ellipsis: bool) -> list[str]: - """Format suffixes after display LCP: group by first hop, nest continuations with U+2500.""" + """Format suffixes after display LCP: group by first hop, in-group LCP on head, nest tails with U+2500.""" non_empty = [s for s in suffix_tokens if s] if not non_empty: return _tree_branch_lines_flat(["..."]) if short_ellipsis else [] @@ -140,10 +149,12 @@ def _shrink_display_lcp(maximal: list[list[str]], lcp: list[str]) -> list[str]: def _format_path_cluster(token_lists: list[list[str]], use_brackets: bool) -> list[str]: - """Format a cluster into condensed lines (common prefix + ├/└ and ├─/└─). + """Format a cluster into condensed lines (common prefix + ┐ + ├/└ and ├─/└─). - Suffixes are grouped by their first hop after the display LCP; multiple variants under the same - hop use one ├ line for that hop and ├─/└─ for continuations. The last line of the block uses └/└─. + The shared path line ends with ┐ (U+2510) when branch lines follow. Suffixes are grouped by + their first hop after the display LCP; within a group, the longest common prefix of all + suffixes in that group is one ├ line, then ├─/└─ for each distinct tail. The last line of the + block uses └/└─. If one path stops exactly where another continues, the displayed LCP is shortened so the shared segment is not mistaken for a single endpoint (e.g. only └ tail after a full shorter path). @@ -166,10 +177,13 @@ def _format_path_cluster(token_lists: list[list[str]], use_brackets: bool) -> li if len(lcp) > 0: suffix_tokens = [t[len(lcp) :] for t in maximal] - lines = [",".join(lcp)] + common = ",".join(lcp) branch_lines = _format_suffix_branch_lines(suffix_tokens, short_ellipsis) if branch_lines: + lines = [f"{common} {_BRANCH_CORNER}"] lines.extend(branch_lines) + else: + lines = [common] return lines if len(maximal) == 1: diff --git a/tests/test_multitest_command.py b/tests/test_multitest_command.py index 9998e51..10829dd 100644 --- a/tests/test_multitest_command.py +++ b/tests/test_multitest_command.py @@ -15,6 +15,7 @@ _LAST = "\u2514" _HORIZ = "\u2500" _CHILD_INTER = f"{_INTER}{_HORIZ} " _CHILD_LAST = f"{_LAST}{_HORIZ} " +_CORNER = "\u2510" # ┐ after common path def _make_bot(): @@ -162,7 +163,7 @@ class TestCondensePathLines: out = _condense_path_lines(paths) expected = "\n".join( [ - "e6,0c,85,82,28,1a,cd,7e", + f"e6,0c,85,82,28,1a,cd,7e {_CORNER}", f"{_INTER} 01", f"{_INTER} 7a", f"{_CHILD_INTER}09", @@ -183,7 +184,7 @@ class TestCondensePathLines: # LCP shrinks so cc is not the whole “trunk” while dd/ee branch off expected = "\n".join( [ - "aa,bb", + f"aa,bb {_CORNER}", f"{_INTER} cc", f"{_CHILD_INTER}dd", f"{_CHILD_LAST}ee", @@ -197,7 +198,7 @@ class TestCondensePathLines: out = _condense_path_lines(paths) expected = "\n".join( [ - "cdf1,7e76", + f"cdf1,7e76 {_CORNER}", f"{_INTER} 0101", f"{_CHILD_LAST}0970", ] @@ -215,7 +216,7 @@ class TestCondensePathLines: out = _condense_path_lines(paths) expected = "\n".join( [ - "cdf119,860cca", + f"cdf119,860cca {_CORNER}", f"{_INTER} 010101", f"{_INTER} e0eed9", f"{_CHILD_LAST}1ed612", @@ -224,20 +225,43 @@ class TestCondensePathLines: assert out == expected def test_divergent_routes_with_shared_mid_prefix(self): - """TRM-style: group by first hop (13) so 01 vs 01,1e nest under ├─.""" + """TRM-style: in-group LCP 13,01 so 1e nests; 83,09 stays its own branch.""" paths = sorted(["41,96,13,01", "41,96,13,01,1e", "41,96,83,09"]) out = _condense_path_lines(paths) expected = "\n".join( [ - "41,96", - f"{_INTER} 13", - f"{_CHILD_INTER}01", - f"{_CHILD_INTER}01,1e", + f"41,96 {_CORNER}", + f"{_INTER} 13,01", + f"{_CHILD_INTER}1e", f"{_LAST} 83,09", ] ) assert out == expected + def test_shared_second_hop_not_shown_as_endpoint(self): + """W7ZOO-style: 96,e0 shared by all variants; endpoints are 01,09,1e and fc,7a — not 96.""" + paths = sorted( + [ + "cc,fe,17,b3,7e,96,e0", + "cc,fe,17,b3,7e,96,e0,01", + "cc,fe,17,b3,7e,96,e0,09", + "cc,fe,17,b3,7e,96,e0,1e", + "cc,fe,17,b3,7e,fc,7a", + ] + ) + out = _condense_path_lines(paths) + expected = "\n".join( + [ + f"cc,fe,17,b3,7e {_CORNER}", + f"{_INTER} 96,e0", + f"{_CHILD_INTER}01", + f"{_CHILD_INTER}09", + f"{_CHILD_INTER}1e", + f"{_LAST} fc,7a", + ] + ) + assert out == expected + def test_mixed_first_hops_nest_per_group(self): """Ill Eagle-style: 01 vs 01,1e share a group; 09 and e0 are separate top-level branches.""" paths = sorted( @@ -251,7 +275,7 @@ class TestCondensePathLines: out = _condense_path_lines(paths) expected = "\n".join( [ - "e2,ab,1f,ef,55,21", + f"e2,ab,1f,ef,55,21 {_CORNER}", f"{_INTER} 01", f"{_CHILD_INTER}1e", f"{_INTER} 09", @@ -271,7 +295,7 @@ class TestCondensePathLines: out = _condense_path_lines(paths) expected = "\n".join( [ - "d38a05,c4a86a,067b75,cafee0,1ffbd6,e8154b", + f"d38a05,c4a86a,067b75,cafee0,1ffbd6,e8154b {_CORNER}", f"{_INTER} 860cca", f"{_CHILD_LAST}010101", ] @@ -292,7 +316,7 @@ class TestCondensePathLines: out = _condense_path_lines(paths) expected = "\n".join( [ - "d38a05,479198,a837bc,7e7662", + f"d38a05,479198,a837bc,7e7662 {_CORNER}", f"{_INTER} e0eed9", f"{_CHILD_INTER}010101", f"{_CHILD_INTER}0970d6",