1- from test .support import (requires , _2G , _4G , gc_collect , cpython_only )
1+ from test .support import (
2+ requires , _2G , _4G , gc_collect , cpython_only , is_emscripten
3+ )
24from test .support .import_helper import import_module
35from test .support .os_helper import TESTFN , unlink
46import unittest
57import os
68import re
79import itertools
10+ import random
811import socket
12+ import string
913import sys
1014import weakref
1115
1418
1519PAGESIZE = mmap .PAGESIZE
1620
21+ tagname_prefix = f'python_{ os .getpid ()} _test_mmap'
22+ def random_tagname (length = 10 ):
23+ suffix = '' .join (random .choices (string .ascii_uppercase , k = length ))
24+ return f'{ tagname_prefix } _{ suffix } '
25+
26+ # Python's mmap module dup()s the file descriptor. Emscripten's FS layer
27+ # does not materialize file changes through a dupped fd to a new mmap.
28+ if is_emscripten :
29+ raise unittest .SkipTest ("incompatible with Emscripten's mmap emulation." )
30+
1731
1832class MmapTests (unittest .TestCase ):
1933
@@ -609,21 +623,23 @@ def test_tagname(self):
609623 data1 = b"0123456789"
610624 data2 = b"abcdefghij"
611625 assert len (data1 ) == len (data2 )
626+ tagname1 = random_tagname ()
627+ tagname2 = random_tagname ()
612628
613629 # Test same tag
614- m1 = mmap .mmap (- 1 , len (data1 ), tagname = "foo" )
630+ m1 = mmap .mmap (- 1 , len (data1 ), tagname = tagname1 )
615631 m1 [:] = data1
616- m2 = mmap .mmap (- 1 , len (data2 ), tagname = "foo" )
632+ m2 = mmap .mmap (- 1 , len (data2 ), tagname = tagname1 )
617633 m2 [:] = data2
618634 self .assertEqual (m1 [:], data2 )
619635 self .assertEqual (m2 [:], data2 )
620636 m2 .close ()
621637 m1 .close ()
622638
623639 # Test different tag
624- m1 = mmap .mmap (- 1 , len (data1 ), tagname = "foo" )
640+ m1 = mmap .mmap (- 1 , len (data1 ), tagname = tagname1 )
625641 m1 [:] = data1
626- m2 = mmap .mmap (- 1 , len (data2 ), tagname = "boo" )
642+ m2 = mmap .mmap (- 1 , len (data2 ), tagname = tagname2 )
627643 m2 [:] = data2
628644 self .assertEqual (m1 [:], data1 )
629645 self .assertEqual (m2 [:], data2 )
@@ -634,17 +650,18 @@ def test_tagname(self):
634650 @unittest .skipUnless (os .name == 'nt' , 'requires Windows' )
635651 def test_sizeof (self ):
636652 m1 = mmap .mmap (- 1 , 100 )
637- tagname = "foo"
653+ tagname = random_tagname ()
638654 m2 = mmap .mmap (- 1 , 100 , tagname = tagname )
639655 self .assertEqual (sys .getsizeof (m2 ),
640656 sys .getsizeof (m1 ) + len (tagname ) + 1 )
641657
642658 @unittest .skipUnless (os .name == 'nt' , 'requires Windows' )
643659 def test_crasher_on_windows (self ):
644660 # Should not crash (Issue 1733986)
645- m = mmap .mmap (- 1 , 1000 , tagname = "foo" )
661+ tagname = random_tagname ()
662+ m = mmap .mmap (- 1 , 1000 , tagname = tagname )
646663 try :
647- mmap .mmap (- 1 , 5000 , tagname = "foo" )[:] # same tagname, but larger size
664+ mmap .mmap (- 1 , 5000 , tagname = tagname )[:] # same tagname, but larger size
648665 except :
649666 pass
650667 m .close ()
@@ -707,7 +724,6 @@ def test_write_returning_the_number_of_bytes_written(self):
707724 self .assertEqual (mm .write (b"yz" ), 2 )
708725 self .assertEqual (mm .write (b"python" ), 6 )
709726
710- @unittest .skipIf (os .name == 'nt' , 'cannot resize anonymous mmaps on Windows' )
711727 def test_resize_past_pos (self ):
712728 m = mmap .mmap (- 1 , 8192 )
713729 self .addCleanup (m .close )
@@ -797,6 +813,80 @@ def test_madvise(self):
797813 self .assertEqual (m .madvise (mmap .MADV_NORMAL , 0 , 2 ), None )
798814 self .assertEqual (m .madvise (mmap .MADV_NORMAL , 0 , size ), None )
799815
816+ @unittest .skipUnless (os .name == 'nt' , 'requires Windows' )
817+ def test_resize_up_when_mapped_to_pagefile (self ):
818+ """If the mmap is backed by the pagefile ensure a resize up can happen
819+ and that the original data is still in place
820+ """
821+ start_size = PAGESIZE
822+ new_size = 2 * start_size
823+ data = bytes (random .getrandbits (8 ) for _ in range (start_size ))
824+
825+ m = mmap .mmap (- 1 , start_size )
826+ m [:] = data
827+ m .resize (new_size )
828+ self .assertEqual (len (m ), new_size )
829+ self .assertEqual (m [:start_size ], data [:start_size ])
830+
831+ @unittest .skipUnless (os .name == 'nt' , 'requires Windows' )
832+ def test_resize_down_when_mapped_to_pagefile (self ):
833+ """If the mmap is backed by the pagefile ensure a resize down up can happen
834+ and that a truncated form of the original data is still in place
835+ """
836+ start_size = PAGESIZE
837+ new_size = start_size // 2
838+ data = bytes (random .getrandbits (8 ) for _ in range (start_size ))
839+
840+ m = mmap .mmap (- 1 , start_size )
841+ m [:] = data
842+ m .resize (new_size )
843+ self .assertEqual (len (m ), new_size )
844+ self .assertEqual (m [:new_size ], data [:new_size ])
845+
846+ @unittest .skipUnless (os .name == 'nt' , 'requires Windows' )
847+ def test_resize_fails_if_mapping_held_elsewhere (self ):
848+ """If more than one mapping is held against a named file on Windows, neither
849+ mapping can be resized
850+ """
851+ start_size = 2 * PAGESIZE
852+ reduced_size = PAGESIZE
853+
854+ f = open (TESTFN , 'wb+' )
855+ f .truncate (start_size )
856+ try :
857+ m1 = mmap .mmap (f .fileno (), start_size )
858+ m2 = mmap .mmap (f .fileno (), start_size )
859+ with self .assertRaises (OSError ):
860+ m1 .resize (reduced_size )
861+ with self .assertRaises (OSError ):
862+ m2 .resize (reduced_size )
863+ m2 .close ()
864+ m1 .resize (reduced_size )
865+ self .assertEqual (m1 .size (), reduced_size )
866+ self .assertEqual (os .stat (f .fileno ()).st_size , reduced_size )
867+ finally :
868+ f .close ()
869+
870+ @unittest .skipUnless (os .name == 'nt' , 'requires Windows' )
871+ def test_resize_succeeds_with_error_for_second_named_mapping (self ):
872+ """If a more than one mapping exists of the same name, none of them can
873+ be resized: they'll raise an Exception and leave the original mapping intact
874+ """
875+ start_size = 2 * PAGESIZE
876+ reduced_size = PAGESIZE
877+ tagname = random_tagname ()
878+ data_length = 8
879+ data = bytes (random .getrandbits (8 ) for _ in range (data_length ))
880+
881+ m1 = mmap .mmap (- 1 , start_size , tagname = tagname )
882+ m2 = mmap .mmap (- 1 , start_size , tagname = tagname )
883+ m1 [:data_length ] = data
884+ self .assertEqual (m2 [:data_length ], data )
885+ with self .assertRaises (OSError ):
886+ m1 .resize (reduced_size )
887+ self .assertEqual (m1 .size (), start_size )
888+ self .assertEqual (m1 [:data_length ], data )
889+ self .assertEqual (m2 [:data_length ], data )
800890
801891class LargeMmapTests (unittest .TestCase ):
802892
0 commit comments