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