4
4
5
5
import unittest , string , sys , struct
6
6
from test import support
7
+ from test .support import import_helper
7
8
from collections import UserList
9
+ import random
8
10
9
11
class Sequence :
10
12
def __init__ (self , seq = 'wxyz' ): self .seq = seq
@@ -79,12 +81,14 @@ class subtype(self.__class__.type2test):
79
81
self .assertIsNot (obj , realresult )
80
82
81
83
# 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 ):
83
85
obj = self .fixtype (obj )
84
86
args = self .fixtype (args )
85
87
with self .assertRaises (exc ) as cm :
86
88
getattr (obj , methodname )(* args )
87
89
self .assertNotEqual (str (cm .exception ), '' )
90
+ if expected_msg is not None :
91
+ self .assertEqual (str (cm .exception ), expected_msg )
88
92
89
93
# call obj.method(*args) without any checks
90
94
def checkcall (self , obj , methodname , * args ):
@@ -317,6 +321,44 @@ def test_rindex(self):
317
321
else :
318
322
self .checkraises (TypeError , 'hello' , 'rindex' , 42 )
319
323
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
+
320
362
def test_lower (self ):
321
363
self .checkequal ('hello' , 'HeLLo' , 'lower' )
322
364
self .checkequal ('hello' , 'hello' , 'lower' )
@@ -428,6 +470,11 @@ def test_split(self):
428
470
self .checkraises (ValueError , 'hello' , 'split' , '' , 0 )
429
471
430
472
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
+
431
478
# by a char
432
479
self .checkequal (['a' , 'b' , 'c' , 'd' ], 'a|b|c|d' , 'rsplit' , '|' )
433
480
self .checkequal (['a|b|c' , 'd' ], 'a|b|c|d' , 'rsplit' , '|' , 1 )
@@ -481,6 +528,9 @@ def test_rsplit(self):
481
528
482
529
# with keyword args
483
530
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 )
484
534
self .checkequal (['a|b|c' , 'd' ],
485
535
'a|b|c|d' , 'rsplit' , '|' , maxsplit = 1 )
486
536
self .checkequal (['a|b|c' , 'd' ],
@@ -506,6 +556,7 @@ def test_replace(self):
506
556
EQ ("" , "" , "replace" , "A" , "" )
507
557
EQ ("" , "" , "replace" , "A" , "A" )
508
558
EQ ("" , "" , "replace" , "" , "" , 100 )
559
+ EQ ("A" , "" , "replace" , "" , "A" , 100 )
509
560
EQ ("" , "" , "replace" , "" , "" , sys .maxsize )
510
561
511
562
# interleave (from=="", 'to' gets inserted everywhere)
@@ -1162,6 +1213,10 @@ def test_subscript(self):
1162
1213
1163
1214
self .checkraises (TypeError , 'abc' , '__getitem__' , 'def' )
1164
1215
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
+
1165
1220
def test_slice (self ):
1166
1221
self .checkequal ('abc' , 'abc' , '__getitem__' , slice (0 , 1000 ))
1167
1222
self .checkequal ('abc' , 'abc' , '__getitem__' , slice (0 , 3 ))
@@ -1188,8 +1243,6 @@ def test_extended_getslice(self):
1188
1243
slice (start , stop , step ))
1189
1244
1190
1245
def test_mul (self ):
1191
- self .assertTrue ("('' * 3) is ''" );
1192
- self .assertTrue ("('a' * 0) is ''" );
1193
1246
self .checkequal ('' , 'abc' , '__mul__' , - 1 )
1194
1247
self .checkequal ('' , 'abc' , '__mul__' , 0 )
1195
1248
self .checkequal ('abc' , 'abc' , '__mul__' , 1 )
@@ -1291,17 +1344,17 @@ class X(object): pass
1291
1344
1292
1345
@support .cpython_only
1293
1346
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
1296
1349
self .checkraises (OverflowError , '%*s' , '__mod__' ,
1297
- (PY_SSIZE_T_MAX + 1 , '' ))
1350
+ (_testcapi . PY_SSIZE_T_MAX + 1 , '' ))
1298
1351
self .checkraises (OverflowError , '%.*f' , '__mod__' ,
1299
- (INT_MAX + 1 , 1. / 7 ))
1352
+ (_testcapi . INT_MAX + 1 , 1. / 7 ))
1300
1353
# Issue 15989
1301
1354
self .checkraises (OverflowError , '%*s' , '__mod__' ,
1302
1355
(SIZE_MAX + 1 , '' ))
1303
1356
self .checkraises (OverflowError , '%.*f' , '__mod__' ,
1304
- (UINT_MAX + 1 , 1. / 7 ))
1357
+ (_testcapi . UINT_MAX + 1 , 1. / 7 ))
1305
1358
1306
1359
def test_floatformatting (self ):
1307
1360
# float formatting
0 commit comments