44
55import unittest , string , sys , struct
66from test import support
7+ from test .support import import_helper
78from collections import UserList
9+ import random
810
911class Sequence :
1012 def __init__ (self , seq = 'wxyz' ): self .seq = seq
@@ -79,12 +81,14 @@ class subtype(self.__class__.type2test):
7981 self .assertIsNot (obj , realresult )
8082
8183 # check that obj.method(*args) raises exc
82- def checkraises (self , exc , obj , methodname , * args ):
84+ def checkraises (self , exc , obj , methodname , * args , expected_msg = None ):
8385 obj = self .fixtype (obj )
8486 args = self .fixtype (args )
8587 with self .assertRaises (exc ) as cm :
8688 getattr (obj , methodname )(* args )
8789 self .assertNotEqual (str (cm .exception ), '' )
90+ if expected_msg is not None :
91+ self .assertEqual (str (cm .exception ), expected_msg )
8892
8993 # call obj.method(*args) without any checks
9094 def checkcall (self , obj , methodname , * args ):
@@ -317,6 +321,44 @@ def test_rindex(self):
317321 else :
318322 self .checkraises (TypeError , 'hello' , 'rindex' , 42 )
319323
324+ def test_find_periodic_pattern (self ):
325+ """Cover the special path for periodic patterns."""
326+ def reference_find (p , s ):
327+ for i in range (len (s )):
328+ if s .startswith (p , i ):
329+ return i
330+ return - 1
331+
332+ rr = random .randrange
333+ choices = random .choices
334+ for _ in range (1000 ):
335+ p0 = '' .join (choices ('abcde' , k = rr (10 ))) * rr (10 , 20 )
336+ p = p0 [:len (p0 ) - rr (10 )] # pop off some characters
337+ left = '' .join (choices ('abcdef' , k = rr (2000 )))
338+ right = '' .join (choices ('abcdef' , k = rr (2000 )))
339+ text = left + p + right
340+ with self .subTest (p = p , text = text ):
341+ self .checkequal (reference_find (p , text ),
342+ text , 'find' , p )
343+
344+ def test_find_shift_table_overflow (self ):
345+ """When the table of 8-bit shifts overflows."""
346+ N = 2 ** 8 + 100
347+
348+ # first check the periodic case
349+ # here, the shift for 'b' is N + 1.
350+ pattern1 = 'a' * N + 'b' + 'a' * N
351+ text1 = 'babbaa' * N + pattern1
352+ self .checkequal (len (text1 )- len (pattern1 ),
353+ text1 , 'find' , pattern1 )
354+
355+ # now check the non-periodic case
356+ # here, the shift for 'd' is 3*(N+1)+1
357+ pattern2 = 'ddd' + 'abc' * N + "eee"
358+ text2 = pattern2 [:- 1 ] + "ddeede" * 2 * N + pattern2 + "de" * N
359+ self .checkequal (len (text2 ) - N * len ("de" ) - len (pattern2 ),
360+ text2 , 'find' , pattern2 )
361+
320362 def test_lower (self ):
321363 self .checkequal ('hello' , 'HeLLo' , 'lower' )
322364 self .checkequal ('hello' , 'hello' , 'lower' )
@@ -428,6 +470,11 @@ def test_split(self):
428470 self .checkraises (ValueError , 'hello' , 'split' , '' , 0 )
429471
430472 def test_rsplit (self ):
473+ # without arg
474+ self .checkequal (['a' , 'b' , 'c' , 'd' ], 'a b c d' , 'rsplit' )
475+ self .checkequal (['a' , 'b' , 'c' , 'd' ], 'a b c d' , 'rsplit' )
476+ self .checkequal ([], '' , 'rsplit' )
477+
431478 # by a char
432479 self .checkequal (['a' , 'b' , 'c' , 'd' ], 'a|b|c|d' , 'rsplit' , '|' )
433480 self .checkequal (['a|b|c' , 'd' ], 'a|b|c|d' , 'rsplit' , '|' , 1 )
@@ -481,6 +528,9 @@ def test_rsplit(self):
481528
482529 # with keyword args
483530 self .checkequal (['a' , 'b' , 'c' , 'd' ], 'a|b|c|d' , 'rsplit' , sep = '|' )
531+ self .checkequal (['a' , 'b' , 'c' , 'd' ], 'a b c d' , 'rsplit' , sep = None )
532+ self .checkequal (['a b c' , 'd' ],
533+ 'a b c d' , 'rsplit' , sep = None , maxsplit = 1 )
484534 self .checkequal (['a|b|c' , 'd' ],
485535 'a|b|c|d' , 'rsplit' , '|' , maxsplit = 1 )
486536 self .checkequal (['a|b|c' , 'd' ],
@@ -506,6 +556,7 @@ def test_replace(self):
506556 EQ ("" , "" , "replace" , "A" , "" )
507557 EQ ("" , "" , "replace" , "A" , "A" )
508558 EQ ("" , "" , "replace" , "" , "" , 100 )
559+ EQ ("A" , "" , "replace" , "" , "A" , 100 )
509560 EQ ("" , "" , "replace" , "" , "" , sys .maxsize )
510561
511562 # interleave (from=="", 'to' gets inserted everywhere)
@@ -1162,6 +1213,10 @@ def test_subscript(self):
11621213
11631214 self .checkraises (TypeError , 'abc' , '__getitem__' , 'def' )
11641215
1216+ for idx_type in ('def' , object ()):
1217+ expected_msg = "string indices must be integers, not '{}'" .format (type (idx_type ).__name__ )
1218+ self .checkraises (TypeError , 'abc' , '__getitem__' , idx_type , expected_msg = expected_msg )
1219+
11651220 def test_slice (self ):
11661221 self .checkequal ('abc' , 'abc' , '__getitem__' , slice (0 , 1000 ))
11671222 self .checkequal ('abc' , 'abc' , '__getitem__' , slice (0 , 3 ))
@@ -1188,8 +1243,6 @@ def test_extended_getslice(self):
11881243 slice (start , stop , step ))
11891244
11901245 def test_mul (self ):
1191- self .assertTrue ("('' * 3) is ''" );
1192- self .assertTrue ("('a' * 0) is ''" );
11931246 self .checkequal ('' , 'abc' , '__mul__' , - 1 )
11941247 self .checkequal ('' , 'abc' , '__mul__' , 0 )
11951248 self .checkequal ('abc' , 'abc' , '__mul__' , 1 )
@@ -1291,17 +1344,17 @@ class X(object): pass
12911344
12921345 @support .cpython_only
12931346 def test_formatting_c_limits (self ):
1294- from _testcapi import PY_SSIZE_T_MAX , INT_MAX , UINT_MAX
1295- SIZE_MAX = (1 << (PY_SSIZE_T_MAX .bit_length () + 1 )) - 1
1347+ _testcapi = import_helper . import_module ( '_testcapi' )
1348+ SIZE_MAX = (1 << (_testcapi . PY_SSIZE_T_MAX .bit_length () + 1 )) - 1
12961349 self .checkraises (OverflowError , '%*s' , '__mod__' ,
1297- (PY_SSIZE_T_MAX + 1 , '' ))
1350+ (_testcapi . PY_SSIZE_T_MAX + 1 , '' ))
12981351 self .checkraises (OverflowError , '%.*f' , '__mod__' ,
1299- (INT_MAX + 1 , 1. / 7 ))
1352+ (_testcapi . INT_MAX + 1 , 1. / 7 ))
13001353 # Issue 15989
13011354 self .checkraises (OverflowError , '%*s' , '__mod__' ,
13021355 (SIZE_MAX + 1 , '' ))
13031356 self .checkraises (OverflowError , '%.*f' , '__mod__' ,
1304- (UINT_MAX + 1 , 1. / 7 ))
1357+ (_testcapi . UINT_MAX + 1 , 1. / 7 ))
13051358
13061359 def test_floatformatting (self ):
13071360 # float formatting
0 commit comments