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
+ )
2
4
from test .support .import_helper import import_module
3
5
from test .support .os_helper import TESTFN , unlink
4
6
import unittest
5
7
import os
6
8
import re
7
9
import itertools
10
+ import random
8
11
import socket
12
+ import string
9
13
import sys
10
14
import weakref
11
15
14
18
15
19
PAGESIZE = mmap .PAGESIZE
16
20
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
+
17
31
18
32
class MmapTests (unittest .TestCase ):
19
33
@@ -609,21 +623,23 @@ def test_tagname(self):
609
623
data1 = b"0123456789"
610
624
data2 = b"abcdefghij"
611
625
assert len (data1 ) == len (data2 )
626
+ tagname1 = random_tagname ()
627
+ tagname2 = random_tagname ()
612
628
613
629
# Test same tag
614
- m1 = mmap .mmap (- 1 , len (data1 ), tagname = "foo" )
630
+ m1 = mmap .mmap (- 1 , len (data1 ), tagname = tagname1 )
615
631
m1 [:] = data1
616
- m2 = mmap .mmap (- 1 , len (data2 ), tagname = "foo" )
632
+ m2 = mmap .mmap (- 1 , len (data2 ), tagname = tagname1 )
617
633
m2 [:] = data2
618
634
self .assertEqual (m1 [:], data2 )
619
635
self .assertEqual (m2 [:], data2 )
620
636
m2 .close ()
621
637
m1 .close ()
622
638
623
639
# Test different tag
624
- m1 = mmap .mmap (- 1 , len (data1 ), tagname = "foo" )
640
+ m1 = mmap .mmap (- 1 , len (data1 ), tagname = tagname1 )
625
641
m1 [:] = data1
626
- m2 = mmap .mmap (- 1 , len (data2 ), tagname = "boo" )
642
+ m2 = mmap .mmap (- 1 , len (data2 ), tagname = tagname2 )
627
643
m2 [:] = data2
628
644
self .assertEqual (m1 [:], data1 )
629
645
self .assertEqual (m2 [:], data2 )
@@ -634,17 +650,18 @@ def test_tagname(self):
634
650
@unittest .skipUnless (os .name == 'nt' , 'requires Windows' )
635
651
def test_sizeof (self ):
636
652
m1 = mmap .mmap (- 1 , 100 )
637
- tagname = "foo"
653
+ tagname = random_tagname ()
638
654
m2 = mmap .mmap (- 1 , 100 , tagname = tagname )
639
655
self .assertEqual (sys .getsizeof (m2 ),
640
656
sys .getsizeof (m1 ) + len (tagname ) + 1 )
641
657
642
658
@unittest .skipUnless (os .name == 'nt' , 'requires Windows' )
643
659
def test_crasher_on_windows (self ):
644
660
# 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 )
646
663
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
648
665
except :
649
666
pass
650
667
m .close ()
@@ -707,7 +724,6 @@ def test_write_returning_the_number_of_bytes_written(self):
707
724
self .assertEqual (mm .write (b"yz" ), 2 )
708
725
self .assertEqual (mm .write (b"python" ), 6 )
709
726
710
- @unittest .skipIf (os .name == 'nt' , 'cannot resize anonymous mmaps on Windows' )
711
727
def test_resize_past_pos (self ):
712
728
m = mmap .mmap (- 1 , 8192 )
713
729
self .addCleanup (m .close )
@@ -797,6 +813,80 @@ def test_madvise(self):
797
813
self .assertEqual (m .madvise (mmap .MADV_NORMAL , 0 , 2 ), None )
798
814
self .assertEqual (m .madvise (mmap .MADV_NORMAL , 0 , size ), None )
799
815
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 )
800
890
801
891
class LargeMmapTests (unittest .TestCase ):
802
892
0 commit comments