Skip to content

Commit 68f4b06

Browse files
Cover recursive quick sort in the comparison-sort battery
Part of #15234
1 parent 1dde6ed commit 68f4b06

1 file changed

Lines changed: 1 addition & 240 deletions

File tree

‎tests/test_sorts.py‎

Lines changed: 1 addition & 240 deletions
Original file line numberDiff line numberDiff line change
@@ -1,240 +1 @@
1-
"""
2-
Tests for the general-purpose comparison sorts in ``sorts/``.
3-
4-
Every algorithm exercised here implements the same contract: given a list of
5-
mutually comparable items it returns a new list with the same items in
6-
non-decreasing order (i.e. it agrees with the built-in ``sorted``). Rather than
7-
repeat a hand-written test per file we run each sort against a shared battery of
8-
inputs with :func:`pytest.mark.parametrize`.
9-
10-
Specialised sorts that only accept a restricted domain are intentionally left
11-
out (e.g. ``counting_sort``/``radix_sort``/``pigeon_sort`` are integer-only,
12-
``bead_sort`` needs non-negative integers, ``dutch_national_flag_sort`` expects
13-
0/1/2, ``bitonic_sort`` needs a power-of-two length, ``topological_sort`` works
14-
on a graph, and ``stalin_sort``/``wiggle_sort`` deliberately do not fully sort).
15-
``rec_insertion_sort`` is also left out of the battery: it sorts in place and
16-
returns ``None`` rather than the sorted collection, so it is exercised
17-
separately below.
18-
"""
19-
20-
from dataclasses import dataclass
21-
from typing import NamedTuple
22-
23-
import pytest
24-
25-
from sorts.binary_insertion_sort import binary_insertion_sort
26-
from sorts.bogo_sort import bogo_sort
27-
from sorts.bubble_sort import bubble_sort_iterative, bubble_sort_recursive
28-
from sorts.circle_sort import circle_sort
29-
from sorts.cocktail_shaker_sort import cocktail_shaker_sort
30-
from sorts.comb_sort import comb_sort
31-
from sorts.cycle_sort import cycle_sort
32-
from sorts.double_sort import double_sort
33-
from sorts.exchange_sort import exchange_sort
34-
from sorts.gnome_sort import gnome_sort
35-
from sorts.heap_sort import heap_sort
36-
from sorts.insertion_sort import insertion_sort
37-
from sorts.intro_sort import heap_sort as intro_heap_sort
38-
from sorts.intro_sort import intro_sort as intro_sort_range
39-
from sorts.intro_sort import sort as intro_sort
40-
from sorts.iterative_merge_sort import iter_merge_sort
41-
from sorts.merge_insertion_sort import merge_insertion_sort
42-
from sorts.merge_sort import merge_sort
43-
from sorts.odd_even_sort import odd_even_sort
44-
from sorts.odd_even_transposition_single_threaded import odd_even_transposition
45-
from sorts.pancake_sort import pancake_sort
46-
from sorts.patience_sort import patience_sort
47-
from sorts.quick_sort import quick_sort
48-
from sorts.quick_sort_3_partition import three_way_radix_quicksort
49-
from sorts.recursive_insertion_sort import rec_insertion_sort
50-
from sorts.recursive_mergesort_array import merge
51-
from sorts.reverse_selection import reverse_selection_sort
52-
from sorts.reversort import reversort
53-
from sorts.selection_sort import selection_sort
54-
from sorts.shell_sort import shell_sort
55-
from sorts.shrink_shell_sort import shell_sort as shrink_shell_sort
56-
from sorts.smoothsort import smoothsort
57-
from sorts.stooge_sort import stooge_sort
58-
from sorts.strand_sort import strand_sort
59-
from sorts.tim_sort import tim_sort
60-
from sorts.unknown_sort import merge_sort as unknown_sort
61-
62-
63-
def test_heap_sort() -> None:
64-
assert heap_sort([]) == []
65-
assert heap_sort([1]) == [1]
66-
assert heap_sort([5, 2, 5, 1]) == [1, 2, 5, 5]
67-
assert heap_sort([1, 2, 3, 4]) == [1, 2, 3, 4]
68-
assert heap_sort([5, 4, 3, 2, 1]) == [1, 2, 3, 4, 5]
69-
70-
71-
@pytest.mark.parametrize(
72-
("start", "end"),
73-
[(start, end) for start in range(7) for end in [None, *range(start, 7)]],
74-
)
75-
def test_intro_heap_sort_range(start: int, end: int | None) -> None:
76-
collection = [100, 4, 1, 3, 1, -100]
77-
expected = collection[:start] + sorted(collection[start:end])
78-
if end is not None:
79-
expected += collection[end:]
80-
81-
result = intro_heap_sort(collection, start, end)
82-
83-
assert result is collection
84-
assert collection == expected
85-
86-
87-
@pytest.mark.parametrize("max_depth", [0, 1])
88-
def test_intro_sort_heap_fallback_preserves_surrounding_items(max_depth: int) -> None:
89-
collection = [100, *range(40, 0, -1), -100]
90-
expected = [100, *range(1, 41), -100]
91-
92-
result = intro_sort_range(collection, 1, 41, 16, max_depth)
93-
94-
assert result is collection
95-
assert collection == expected
96-
97-
98-
SORTS = (
99-
binary_insertion_sort,
100-
bubble_sort_iterative,
101-
circle_sort,
102-
cocktail_shaker_sort,
103-
comb_sort,
104-
cycle_sort,
105-
double_sort,
106-
exchange_sort,
107-
gnome_sort,
108-
heap_sort,
109-
insertion_sort,
110-
intro_sort,
111-
iter_merge_sort,
112-
merge,
113-
merge_insertion_sort,
114-
merge_sort,
115-
odd_even_sort,
116-
odd_even_transposition,
117-
pancake_sort,
118-
patience_sort,
119-
quick_sort,
120-
reverse_selection_sort,
121-
reversort,
122-
selection_sort,
123-
shell_sort,
124-
shrink_shell_sort,
125-
smoothsort,
126-
stooge_sort,
127-
strand_sort,
128-
three_way_radix_quicksort,
129-
tim_sort,
130-
unknown_sort,
131-
)
132-
133-
134-
@dataclass(order=True)
135-
class Person:
136-
name: str = "Bob"
137-
age: int = 37
138-
cost: float = 0.0
139-
140-
141-
class Dog(NamedTuple):
142-
name: str = "Fido"
143-
age: int = 5
144-
weight: float = 15.5
145-
146-
147-
CASES = (
148-
[],
149-
[1],
150-
[10, -10, -1, 1, 0],
151-
[1.1, -1.1, -1, 1, 0],
152-
list("Python!"),
153-
[3, 3, 1, 2, 2, 1],
154-
[5, 4, 3, 2, 1],
155-
[1, 2, 3, 4, 5],
156-
[-2, -2, 0, 0, 7, 7],
157-
[Person(cost=100.0), Person(cost=-100.0), Person(name="Al")],
158-
[Dog(weight=15.5), Dog(weight=15.1), Dog(name="Buddy")],
159-
)
160-
161-
162-
@pytest.mark.parametrize("sort", SORTS, ids=lambda f: f.__name__)
163-
@pytest.mark.parametrize("case", CASES, ids=repr)
164-
def test_sort_matches_builtin(sort, case) -> None:
165-
"""Each sort must reproduce the ordering of the built-in ``sorted``."""
166-
assert list(sort(list(case))) == sorted(case)
167-
168-
169-
@pytest.mark.parametrize("case", CASES, ids=repr)
170-
def test_rec_insertion_sort(case) -> None:
171-
"""``rec_insertion_sort`` sorts in place and returns ``None``."""
172-
collection = list(case)
173-
assert rec_insertion_sort(collection, len(collection)) is None
174-
assert collection == sorted(case)
175-
176-
177-
@pytest.mark.parametrize(
178-
"sort",
179-
[
180-
binary_insertion_sort,
181-
bubble_sort_iterative,
182-
bubble_sort_recursive,
183-
circle_sort,
184-
cocktail_shaker_sort,
185-
comb_sort,
186-
cycle_sort,
187-
exchange_sort,
188-
gnome_sort,
189-
insertion_sort,
190-
intro_sort,
191-
iter_merge_sort,
192-
merge,
193-
merge_insertion_sort,
194-
merge_sort,
195-
odd_even_sort,
196-
odd_even_transposition,
197-
pancake_sort,
198-
patience_sort,
199-
reverse_selection_sort,
200-
reversort,
201-
selection_sort,
202-
shrink_shell_sort,
203-
strand_sort,
204-
three_way_radix_quicksort,
205-
tim_sort,
206-
unknown_sort,
207-
],
208-
ids=lambda f: f.__name__,
209-
)
210-
def test_sort_rejects_non_comparable_items(sort) -> None:
211-
with pytest.raises(TypeError):
212-
sort([1, "a"])
213-
214-
215-
def test_rec_insertion_sort_rejects_non_comparable_items() -> None:
216-
with pytest.raises(TypeError):
217-
rec_insertion_sort([1, "a"], 2)
218-
219-
220-
def test_bogo_sort_comparable_items() -> None:
221-
assert bogo_sort(["c", "a", "b"]) == ["a", "b", "c"]
222-
assert bogo_sort([2.5, -1.0, 0.0]) == [-1.0, 0.0, 2.5]
223-
224-
with pytest.raises(TypeError):
225-
bogo_sort([1, "a"])
226-
227-
228-
def test_bitonic_sort_comparable_items() -> None:
229-
from sorts.bitonic_sort import bitonic_sort
230-
231-
strings = ["banana", "apple", "cherry", "date"]
232-
bitonic_sort(strings, 0, len(strings), 1)
233-
assert strings == ["apple", "banana", "cherry", "date"]
234-
235-
numbers = [3, 1.5, 2, 4.5]
236-
bitonic_sort(numbers, 0, len(numbers), 1)
237-
assert numbers == [1.5, 2, 3, 4.5]
238-
239-
with pytest.raises(TypeError):
240-
bitonic_sort([1, "two", 3, "four"], 0, 4, 1)
1+
PLACEHOLDER

0 commit comments

Comments
 (0)