Skip to content

Commit 1b588c4

Browse files
committed
Added tests.
1 parent 8afc374 commit 1b588c4

File tree

1 file changed

+120
-0
lines changed

1 file changed

+120
-0
lines changed

tests/test_completion.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
Completions,
2020
utils,
2121
)
22+
from cmd2.completion import all_display_numeric
2223

2324
from .conftest import (
2425
normalize,
@@ -877,6 +878,125 @@ def test_completions_iteration() -> None:
877878
assert list(reversed(completions)) == items[::-1]
878879

879880

881+
def test_numeric_sorting() -> None:
882+
"""Test that numbers and numeric strings are sorted numerically."""
883+
numbers = [5, 6, 4, 3, 7.2, 9.1]
884+
completions = Completions.from_values(numbers)
885+
assert [item.value for item in completions] == sorted(numbers)
886+
887+
number_strs = ["5", "6", "4", "3", "7.2", "9.1"]
888+
completions = Completions.from_values(number_strs)
889+
assert list(completions.to_strings()) == sorted(number_strs, key=float)
890+
891+
mixed = ["5", "6", "4", 3, "7.2", 9.1]
892+
completions = Completions.from_values(mixed)
893+
assert list(completions.to_strings()) == [str(v) for v in sorted(number_strs, key=float)]
894+
895+
896+
def test_is_sorted() -> None:
897+
"""Test that already sorted results are not re-sorted."""
898+
values = [5, 6, 4, 3]
899+
already_sorted = Completions.from_values(values, is_sorted=True)
900+
sorted_on_creation = Completions.from_values(values, is_sorted=False)
901+
902+
assert already_sorted.to_strings() != sorted_on_creation.to_strings()
903+
assert [item.value for item in already_sorted] == values
904+
905+
906+
@pytest.mark.parametrize(
907+
('values', 'all_nums'),
908+
[
909+
([2, 3], True),
910+
([2, 3.7], True),
911+
([2, "3"], True),
912+
([2.2, "3.4"], True),
913+
([2, "3g"], False),
914+
# The display_plain field strips off ANSI sequences
915+
(["\x1b[31m5\x1b[0m", "\x1b[32m9.2\x1b[0m"], True),
916+
(["\x1b[31mNOT_STRING\x1b[0m", "\x1b[32m9.2\x1b[0m"], False),
917+
],
918+
)
919+
def test_all_display_numeric(values: list[int | float | str], all_nums: bool) -> None:
920+
"""Test that all_display_numeric() evaluates the display_plain field."""
921+
922+
items = [CompletionItem(v) for v in values]
923+
assert all_display_numeric(items) == all_nums
924+
925+
926+
def test_remove_duplicates() -> None:
927+
"""Test that duplicate CompletionItems are removed."""
928+
929+
# Create items which alter the fields used in CompletionItem.__eq__().
930+
orig_item = CompletionItem(value="orig item", display="orig display", display_meta="orig meta")
931+
new_value = dataclasses.replace(orig_item, value="new value")
932+
new_text = dataclasses.replace(orig_item, text="new text")
933+
new_display = dataclasses.replace(orig_item, display="new display")
934+
new_meta = dataclasses.replace(orig_item, display_meta="new meta")
935+
936+
# Include each item twice.
937+
items = [orig_item, orig_item, new_value, new_value, new_text, new_text, new_display, new_display, new_meta, new_meta]
938+
completions = Completions(items)
939+
940+
# Make sure we have exactly 1 of each item.
941+
assert len(completions) == 5
942+
assert orig_item in completions
943+
assert new_value in completions
944+
assert new_text in completions
945+
assert new_display in completions
946+
assert new_meta in completions
947+
948+
949+
def test_plain_fields() -> None:
950+
"""Test the plain text fields in CompletionItem."""
951+
display = "\x1b[31mApple\x1b[0m"
952+
display_meta = "\x1b[32mA tasty apple\x1b[0m"
953+
954+
# Show that the plain fields remove the ANSI sequences.
955+
completion_item = CompletionItem("apple", display=display, display_meta=display_meta)
956+
assert completion_item.display == display
957+
assert completion_item.display_plain == "Apple"
958+
assert completion_item.display_meta == display_meta
959+
assert completion_item.display_meta_plain == "A tasty apple"
960+
961+
962+
def test_styled_completion_sort() -> None:
963+
"""Test that sorting is done with the display_plain field."""
964+
965+
# First sort with strings that include ANSI style sequences.
966+
red_apple = "\x1b[31mApple\x1b[0m"
967+
green_cherry = "\x1b[32mCherry\x1b[0m"
968+
blue_banana = "\x1b[34mBanana\x1b[0m"
969+
970+
# This sorts by ASCII: [31m (Red), [32m (Green), [34m (Blue)
971+
unsorted_strs = [blue_banana, red_apple, green_cherry]
972+
sorted_strs = sorted(unsorted_strs, key=utils.DEFAULT_STR_SORT_KEY)
973+
assert sorted_strs == [red_apple, green_cherry, blue_banana]
974+
975+
# Now create a Completions object with these values.
976+
unsorted_items = [
977+
CompletionItem("banana", display=blue_banana),
978+
CompletionItem("cherry", display=green_cherry),
979+
CompletionItem("apple", display=red_apple),
980+
]
981+
982+
completions = Completions(unsorted_items)
983+
984+
# Expected order: Apple (A), Banana (B), Cherry (C)
985+
expected_plain = ["Apple", "Banana", "Cherry"]
986+
expected_styled = [red_apple, blue_banana, green_cherry]
987+
988+
for index, item in enumerate(completions):
989+
# Prove the ANSI stripping worked correctly
990+
assert item.display_plain == expected_plain[index]
991+
992+
# Prove the sort order used the plain text, not the ANSI codes
993+
assert item.display == expected_styled[index]
994+
995+
# Prove the order of completions is not the same as the raw string sort order
996+
completion_displays = [item.display for item in completions]
997+
assert completion_displays != sorted_strs
998+
999+
8801000
# Used by redirect_complete tests
8811001
class RedirCompType(enum.Enum):
8821002
SHELL_CMD = (1,)

0 commit comments

Comments
 (0)