@@ -875,8 +875,70 @@ def run_re_tests():
875875 print ('=== Fails on unicode-sensitive match' , t )
876876
877877
878+ class ReCacheTests (unittest .TestCase ):
879+ """These tests are specific to the re._shrink_cache implementation."""
880+
881+ def setUp (self ):
882+ self ._orig_maxcache = re ._MAXCACHE
883+
884+ def tearDown (self ):
885+ re ._MAXCACHE = self ._orig_maxcache
886+
887+ def test_compile_cache_overflow (self ):
888+ # NOTE: If a profiler or debugger is tracing code and compiling
889+ # regular expressions while tracing through this test... expect
890+ # the test to fail. This test is not concurrency safe.
891+
892+ # Explicitly fill the caches.
893+ re ._MAXCACHE = 20
894+ max_cache = re ._MAXCACHE
895+ unique_chars = tuple (chr (char_num ) for char_num in
896+ range (b'a' [0 ], b'a' [0 ]+ max_cache ))
897+ re ._cache .clear ()
898+ for char in unique_chars :
899+ re ._compile (char , 0 )
900+ self .assertEqual (max_cache , len (re ._cache ))
901+ re ._cache_repl .clear ()
902+ for char in unique_chars :
903+ re ._compile_repl (char * 2 , char )
904+ self .assertEqual (max_cache , len (re ._cache_repl ))
905+
906+ # Overflow both caches and make sure they have extra room left
907+ # afterwards as well as having more than a single entry.
908+ re ._compile ('A' , 0 )
909+ self .assertLess (len (re ._cache ), max_cache )
910+ self .assertGreater (len (re ._cache ), 1 )
911+ re ._compile_repl ('A' , 'A' )
912+ self .assertLess (len (re ._cache_repl ), max_cache )
913+ self .assertGreater (len (re ._cache_repl ), 1 )
914+
915+ def test_shrink_cache_at_limit (self ):
916+ cache = dict (zip (range (6 ), range (6 )))
917+ re ._shrink_cache (cache , 6 , divisor = 3 )
918+ self .assertEqual (4 , len (cache ))
919+
920+ def test_shrink_cache_empty (self ):
921+ cache = {}
922+ re ._shrink_cache (cache , 6 , divisor = 3 )
923+ # Cache was empty, make sure we didn't raise an exception.
924+ self .assertEqual (0 , len (cache ))
925+
926+ def test_shrink_cache_overflowing (self ):
927+ cache = dict (zip (range (6 ), range (6 )))
928+ re ._shrink_cache (cache , 4 , divisor = 2 )
929+ # Cache was larger than the maximum, be sure we shrunk to smaller.
930+ self .assertEqual (2 , len (cache ))
931+
932+ def test_shrink_cache_underflow (self ):
933+ cache = dict (zip (range (6 ), range (6 )))
934+ # No shrinking to do.
935+ re ._shrink_cache (cache , 9 , divisor = 3 )
936+ self .assertEqual (6 , len (cache ))
937+
938+
878939def test_main ():
879940 run_unittest (ReTests )
941+ run_unittest (ReCacheTests )
880942 run_re_tests ()
881943
882944if __name__ == "__main__" :
0 commit comments