@@ -142,6 +142,111 @@ def test_substitutions_with_regex_chars(self):
142142 self .assertEqual (m .sub ('The cat.+ is purple' ), 'The kedi is mor' )
143143
144144
145+ def test_human_readable_list ():
146+ """Test the human_readable_list function with various inputs."""
147+
148+ # Test empty list
149+ assert strutils .human_readable_list ([]) == ''
150+
151+ # Test single item
152+ assert strutils .human_readable_list (['apple' ]) == 'apple'
153+
154+ # Test two items (no Oxford comma applies)
155+ assert strutils .human_readable_list (['apple' , 'banana' ]) == 'apple and banana'
156+
157+ # Test three items with Oxford comma (default)
158+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' ]) == 'apple, banana, and cherry'
159+
160+ # Test three items without Oxford comma
161+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' ], oxford = False ) == 'apple, banana and cherry'
162+
163+ # Test four items with Oxford comma
164+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' , 'date' ]) == 'apple, banana, cherry, and date'
165+
166+ # Test four items without Oxford comma
167+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' , 'date' ], oxford = False ) == 'apple, banana, cherry and date'
168+
169+ # Test custom delimiter
170+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' ], delimiter = ';' ) == 'apple; banana; and cherry'
171+
172+ # Test custom conjunction
173+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' ], conjunction = 'or' ) == 'apple, banana, or cherry'
174+
175+ # Test custom delimiter and conjunction
176+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' ], delimiter = '|' , conjunction = 'plus' ) == 'apple| banana| plus cherry'
177+
178+ # Test custom conjunction without Oxford comma
179+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' ], conjunction = 'or' , oxford = False ) == 'apple, banana or cherry'
180+
181+ # Test delimiter with extra spaces (should be stripped)
182+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' ], delimiter = ' , ' ) == 'apple, banana, and cherry'
183+
184+ # Test conjunction with extra spaces (should be stripped)
185+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' ], conjunction = ' or ' ) == 'apple, banana, or cherry'
186+
187+ # Test with empty strings in the list
188+ assert strutils .human_readable_list (['apple' , '' , 'cherry' ]) == 'apple, , and cherry'
189+
190+ # Test with whitespace strings
191+ assert strutils .human_readable_list (['apple' , ' ' , 'cherry' ]) == 'apple, , and cherry'
192+
193+ # Test with special characters
194+ assert strutils .human_readable_list (['apple & pear' , 'banana/plantain' , 'cherry-bomb' ]) == 'apple & pear, banana/plantain, and cherry-bomb'
195+
196+ # Test with unicode characters
197+ assert strutils .human_readable_list (['🍎' , '🍌' , '🍒' ]) == '🍎, 🍌, and 🍒'
198+
199+ # Test with numbers as strings
200+ assert strutils .human_readable_list (['1' , '2' , '3' ]) == '1, 2, and 3'
201+
202+ # Test edge case with only delimiter character as items
203+ assert strutils .human_readable_list ([',' , ',' , ',' ]) == ',, ,, and ,'
204+
205+ # Test long list to ensure pattern consistency
206+ long_list = ['a' , 'b' , 'c' , 'd' , 'e' , 'f' ]
207+ assert strutils .human_readable_list (long_list ) == 'a, b, c, d, e, and f'
208+ assert strutils .human_readable_list (long_list , oxford = False ) == 'a, b, c, d, e and f'
209+
210+ # Edge cases
211+ # Test with empty delimiter
212+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' ], delimiter = '' ) == 'applebananaand cherry'
213+
214+ # Test with empty conjunction
215+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' ], conjunction = '' ) == 'apple, banana, cherry'
216+
217+ # Test two items with custom delimiter and conjunction
218+ assert strutils .human_readable_list (['apple' , 'banana' ], delimiter = ';' , conjunction = 'or' ) == 'apple or banana'
219+
220+ # Test single item with custom parameters (should ignore them)
221+ assert strutils .human_readable_list (['apple' ], delimiter = '|' , conjunction = 'plus' , oxford = False ) == 'apple'
222+
223+ # Test very long strings
224+ long_item1 = 'a' * 100
225+ long_item2 = 'b' * 100
226+ result = strutils .human_readable_list ([long_item1 , long_item2 ])
227+ assert result == f'{ long_item1 } and { long_item2 } '
228+
229+
230+ def test_human_readable_list_type_annotations ():
231+ """Test that the function works with different sequence types."""
232+
233+ # Test with tuple
234+ assert strutils .human_readable_list (('apple' , 'banana' , 'cherry' )) == 'apple, banana, and cherry'
235+
236+ # Test with list (already tested above, but for completeness)
237+ assert strutils .human_readable_list (['apple' , 'banana' , 'cherry' ]) == 'apple, banana, and cherry'
238+
239+ # Test with generator (converted to list for the test)
240+ def fruit_generator ():
241+ yield 'apple'
242+ yield 'banana'
243+ yield 'cherry'
244+
245+ # Convert generator to list since the function expects a Sequence
246+ fruits = list (fruit_generator ())
247+ assert strutils .human_readable_list (fruits ) == 'apple, banana, and cherry'
248+
249+
145250def test_roundzip ():
146251 aaa = b'a' * 10000
147252 assert strutils .gunzip_bytes (strutils .gzip_bytes (aaa )) == aaa
0 commit comments