• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1from test.test_support import (TESTFN, run_unittest, import_module, unlink,
2                               requires, _2G, _4G, gc_collect, cpython_only)
3import unittest
4import os, re, itertools, socket, sys
5
6mmap = import_module('mmap')
7
8PAGESIZE = mmap.PAGESIZE
9
10class MmapTests(unittest.TestCase):
11
12    def setUp(self):
13        if os.path.exists(TESTFN):
14            os.unlink(TESTFN)
15
16    def tearDown(self):
17        try:
18            os.unlink(TESTFN)
19        except OSError:
20            pass
21
22    def test_basic(self):
23        # Test mmap module on Unix systems and Windows
24
25        # Create a file to be mmap'ed.
26        f = open(TESTFN, 'w+')
27        try:
28            # Write 2 pages worth of data to the file
29            f.write('\0'* PAGESIZE)
30            f.write('foo')
31            f.write('\0'* (PAGESIZE-3) )
32            f.flush()
33            m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
34            f.close()
35
36            # Simple sanity checks
37
38            tp = str(type(m))  # SF bug 128713:  segfaulted on Linux
39            self.assertEqual(m.find('foo'), PAGESIZE)
40
41            self.assertEqual(len(m), 2*PAGESIZE)
42
43            self.assertEqual(m[0], '\0')
44            self.assertEqual(m[0:3], '\0\0\0')
45
46            # Shouldn't crash on boundary (Issue #5292)
47            self.assertRaises(IndexError, m.__getitem__, len(m))
48            self.assertRaises(IndexError, m.__setitem__, len(m), '\0')
49
50            # Modify the file's content
51            m[0] = '3'
52            m[PAGESIZE +3: PAGESIZE +3+3] = 'bar'
53
54            # Check that the modification worked
55            self.assertEqual(m[0], '3')
56            self.assertEqual(m[0:3], '3\0\0')
57            self.assertEqual(m[PAGESIZE-1 : PAGESIZE + 7], '\0foobar\0')
58
59            m.flush()
60
61            # Test doing a regular expression match in an mmap'ed file
62            match = re.search('[A-Za-z]+', m)
63            if match is None:
64                self.fail('regex match on mmap failed!')
65            else:
66                start, end = match.span(0)
67                length = end - start
68
69                self.assertEqual(start, PAGESIZE)
70                self.assertEqual(end, PAGESIZE + 6)
71
72            # test seeking around (try to overflow the seek implementation)
73            m.seek(0,0)
74            self.assertEqual(m.tell(), 0)
75            m.seek(42,1)
76            self.assertEqual(m.tell(), 42)
77            m.seek(0,2)
78            self.assertEqual(m.tell(), len(m))
79
80            # Try to seek to negative position...
81            self.assertRaises(ValueError, m.seek, -1)
82
83            # Try to seek beyond end of mmap...
84            self.assertRaises(ValueError, m.seek, 1, 2)
85
86            # Try to seek to negative position...
87            self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
88
89            # Try resizing map
90            try:
91                m.resize(512)
92            except SystemError:
93                # resize() not supported
94                # No messages are printed, since the output of this test suite
95                # would then be different across platforms.
96                pass
97            else:
98                # resize() is supported
99                self.assertEqual(len(m), 512)
100                # Check that we can no longer seek beyond the new size.
101                self.assertRaises(ValueError, m.seek, 513, 0)
102
103                # Check that the underlying file is truncated too
104                # (bug #728515)
105                f = open(TESTFN)
106                f.seek(0, 2)
107                self.assertEqual(f.tell(), 512)
108                f.close()
109                self.assertEqual(m.size(), 512)
110
111            m.close()
112
113        finally:
114            try:
115                f.close()
116            except OSError:
117                pass
118
119    def test_access_parameter(self):
120        # Test for "access" keyword parameter
121        mapsize = 10
122        open(TESTFN, "wb").write("a"*mapsize)
123        f = open(TESTFN, "rb")
124        m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_READ)
125        self.assertEqual(m[:], 'a'*mapsize, "Readonly memory map data incorrect.")
126
127        # Ensuring that readonly mmap can't be slice assigned
128        try:
129            m[:] = 'b'*mapsize
130        except TypeError:
131            pass
132        else:
133            self.fail("Able to write to readonly memory map")
134
135        # Ensuring that readonly mmap can't be item assigned
136        try:
137            m[0] = 'b'
138        except TypeError:
139            pass
140        else:
141            self.fail("Able to write to readonly memory map")
142
143        # Ensuring that readonly mmap can't be write() to
144        try:
145            m.seek(0,0)
146            m.write('abc')
147        except TypeError:
148            pass
149        else:
150            self.fail("Able to write to readonly memory map")
151
152        # Ensuring that readonly mmap can't be write_byte() to
153        try:
154            m.seek(0,0)
155            m.write_byte('d')
156        except TypeError:
157            pass
158        else:
159            self.fail("Able to write to readonly memory map")
160
161        # Ensuring that readonly mmap can't be resized
162        try:
163            m.resize(2*mapsize)
164        except SystemError:   # resize is not universally supported
165            pass
166        except TypeError:
167            pass
168        else:
169            self.fail("Able to resize readonly memory map")
170        f.close()
171        del m, f
172        self.assertEqual(open(TESTFN, "rb").read(), 'a'*mapsize,
173               "Readonly memory map data file was modified")
174
175        # Opening mmap with size too big
176        import sys
177        f = open(TESTFN, "r+b")
178        try:
179            m = mmap.mmap(f.fileno(), mapsize+1)
180        except ValueError:
181            # we do not expect a ValueError on Windows
182            # CAUTION:  This also changes the size of the file on disk, and
183            # later tests assume that the length hasn't changed.  We need to
184            # repair that.
185            if sys.platform.startswith('win'):
186                self.fail("Opening mmap with size+1 should work on Windows.")
187        else:
188            # we expect a ValueError on Unix, but not on Windows
189            if not sys.platform.startswith('win'):
190                self.fail("Opening mmap with size+1 should raise ValueError.")
191            m.close()
192        f.close()
193        if sys.platform.startswith('win'):
194            # Repair damage from the resizing test.
195            f = open(TESTFN, 'r+b')
196            f.truncate(mapsize)
197            f.close()
198
199        # Opening mmap with access=ACCESS_WRITE
200        f = open(TESTFN, "r+b")
201        m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE)
202        # Modifying write-through memory map
203        m[:] = 'c'*mapsize
204        self.assertEqual(m[:], 'c'*mapsize,
205               "Write-through memory map memory not updated properly.")
206        m.flush()
207        m.close()
208        f.close()
209        f = open(TESTFN, 'rb')
210        stuff = f.read()
211        f.close()
212        self.assertEqual(stuff, 'c'*mapsize,
213               "Write-through memory map data file not updated properly.")
214
215        # Opening mmap with access=ACCESS_COPY
216        f = open(TESTFN, "r+b")
217        m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY)
218        # Modifying copy-on-write memory map
219        m[:] = 'd'*mapsize
220        self.assertEqual(m[:], 'd' * mapsize,
221               "Copy-on-write memory map data not written correctly.")
222        m.flush()
223        self.assertEqual(open(TESTFN, "rb").read(), 'c'*mapsize,
224               "Copy-on-write test data file should not be modified.")
225        # Ensuring copy-on-write maps cannot be resized
226        self.assertRaises(TypeError, m.resize, 2*mapsize)
227        f.close()
228        del m, f
229
230        # Ensuring invalid access parameter raises exception
231        f = open(TESTFN, "r+b")
232        self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4)
233        f.close()
234
235        if os.name == "posix":
236            # Try incompatible flags, prot and access parameters.
237            f = open(TESTFN, "r+b")
238            self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize,
239                              flags=mmap.MAP_PRIVATE,
240                              prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE)
241            f.close()
242
243            # Try writing with PROT_EXEC and without PROT_WRITE
244            prot = mmap.PROT_READ | getattr(mmap, 'PROT_EXEC', 0)
245            with open(TESTFN, "r+b") as f:
246                m = mmap.mmap(f.fileno(), mapsize, prot=prot)
247                self.assertRaises(TypeError, m.write, b"abcdef")
248                self.assertRaises(TypeError, m.write_byte, 0)
249                m.close()
250
251    def test_bad_file_desc(self):
252        # Try opening a bad file descriptor...
253        self.assertRaises(mmap.error, mmap.mmap, -2, 4096)
254
255    def test_tougher_find(self):
256        # Do a tougher .find() test.  SF bug 515943 pointed out that, in 2.2,
257        # searching for data with embedded \0 bytes didn't work.
258        f = open(TESTFN, 'w+')
259
260        data = 'aabaac\x00deef\x00\x00aa\x00'
261        n = len(data)
262        f.write(data)
263        f.flush()
264        m = mmap.mmap(f.fileno(), n)
265        f.close()
266
267        for start in range(n+1):
268            for finish in range(start, n+1):
269                slice = data[start : finish]
270                self.assertEqual(m.find(slice), data.find(slice))
271                self.assertEqual(m.find(slice + 'x'), -1)
272        m.close()
273
274    def test_find_end(self):
275        # test the new 'end' parameter works as expected
276        f = open(TESTFN, 'w+')
277        data = 'one two ones'
278        n = len(data)
279        f.write(data)
280        f.flush()
281        m = mmap.mmap(f.fileno(), n)
282        f.close()
283
284        self.assertEqual(m.find('one'), 0)
285        self.assertEqual(m.find('ones'), 8)
286        self.assertEqual(m.find('one', 0, -1), 0)
287        self.assertEqual(m.find('one', 1), 8)
288        self.assertEqual(m.find('one', 1, -1), 8)
289        self.assertEqual(m.find('one', 1, -2), -1)
290
291
292    def test_rfind(self):
293        # test the new 'end' parameter works as expected
294        f = open(TESTFN, 'w+')
295        data = 'one two ones'
296        n = len(data)
297        f.write(data)
298        f.flush()
299        m = mmap.mmap(f.fileno(), n)
300        f.close()
301
302        self.assertEqual(m.rfind('one'), 8)
303        self.assertEqual(m.rfind('one '), 0)
304        self.assertEqual(m.rfind('one', 0, -1), 8)
305        self.assertEqual(m.rfind('one', 0, -2), 0)
306        self.assertEqual(m.rfind('one', 1, -1), 8)
307        self.assertEqual(m.rfind('one', 1, -2), -1)
308
309
310    def test_double_close(self):
311        # make sure a double close doesn't crash on Solaris (Bug# 665913)
312        f = open(TESTFN, 'w+')
313
314        f.write(2**16 * 'a') # Arbitrary character
315        f.close()
316
317        f = open(TESTFN)
318        mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
319        mf.close()
320        mf.close()
321        f.close()
322
323    @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
324    def test_entire_file(self):
325        # test mapping of entire file by passing 0 for map length
326        f = open(TESTFN, "w+")
327
328        f.write(2**16 * 'm') # Arbitrary character
329        f.close()
330
331        f = open(TESTFN, "rb+")
332        mf = mmap.mmap(f.fileno(), 0)
333        self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
334        self.assertEqual(mf.read(2**16), 2**16 * "m")
335        mf.close()
336        f.close()
337
338    @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
339    def test_length_0_offset(self):
340        # Issue #10916: test mapping of remainder of file by passing 0 for
341        # map length with an offset doesn't cause a segfault.
342        # NOTE: allocation granularity is currently 65536 under Win64,
343        # and therefore the minimum offset alignment.
344        with open(TESTFN, "wb") as f:
345            f.write((65536 * 2) * b'm') # Arbitrary character
346
347        with open(TESTFN, "rb") as f:
348            mf = mmap.mmap(f.fileno(), 0, offset=65536, access=mmap.ACCESS_READ)
349            try:
350                self.assertRaises(IndexError, mf.__getitem__, 80000)
351            finally:
352                mf.close()
353
354    @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
355    def test_length_0_large_offset(self):
356        # Issue #10959: test mapping of a file by passing 0 for
357        # map length with a large offset doesn't cause a segfault.
358        with open(TESTFN, "wb") as f:
359            f.write(115699 * b'm') # Arbitrary character
360
361        with open(TESTFN, "w+b") as f:
362            self.assertRaises(ValueError, mmap.mmap, f.fileno(), 0,
363                              offset=2147418112)
364
365    def test_move(self):
366        # make move works everywhere (64-bit format problem earlier)
367        f = open(TESTFN, 'w+')
368
369        f.write("ABCDEabcde") # Arbitrary character
370        f.flush()
371
372        mf = mmap.mmap(f.fileno(), 10)
373        mf.move(5, 0, 5)
374        self.assertEqual(mf[:], "ABCDEABCDE", "Map move should have duplicated front 5")
375        mf.close()
376        f.close()
377
378        # more excessive test
379        data = "0123456789"
380        for dest in range(len(data)):
381            for src in range(len(data)):
382                for count in range(len(data) - max(dest, src)):
383                    expected = data[:dest] + data[src:src+count] + data[dest+count:]
384                    m = mmap.mmap(-1, len(data))
385                    m[:] = data
386                    m.move(dest, src, count)
387                    self.assertEqual(m[:], expected)
388                    m.close()
389
390        # segfault test (Issue 5387)
391        m = mmap.mmap(-1, 100)
392        offsets = [-100, -1, 0, 1, 100]
393        for source, dest, size in itertools.product(offsets, offsets, offsets):
394            try:
395                m.move(source, dest, size)
396            except ValueError:
397                pass
398
399        offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
400                   (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
401        for source, dest, size in offsets:
402            self.assertRaises(ValueError, m.move, source, dest, size)
403
404        m.close()
405
406        m = mmap.mmap(-1, 1) # single byte
407        self.assertRaises(ValueError, m.move, 0, 0, 2)
408        self.assertRaises(ValueError, m.move, 1, 0, 1)
409        self.assertRaises(ValueError, m.move, 0, 1, 1)
410        m.move(0, 0, 1)
411        m.move(0, 0, 0)
412
413
414    def test_anonymous(self):
415        # anonymous mmap.mmap(-1, PAGE)
416        m = mmap.mmap(-1, PAGESIZE)
417        for x in xrange(PAGESIZE):
418            self.assertEqual(m[x], '\0', "anonymously mmap'ed contents should be zero")
419
420        for x in xrange(PAGESIZE):
421            m[x] = ch = chr(x & 255)
422            self.assertEqual(m[x], ch)
423
424    def test_extended_getslice(self):
425        # Test extended slicing by comparing with list slicing.
426        s = "".join(chr(c) for c in reversed(range(256)))
427        m = mmap.mmap(-1, len(s))
428        m[:] = s
429        self.assertEqual(m[:], s)
430        indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
431        for start in indices:
432            for stop in indices:
433                # Skip step 0 (invalid)
434                for step in indices[1:]:
435                    self.assertEqual(m[start:stop:step],
436                                     s[start:stop:step])
437
438    def test_extended_set_del_slice(self):
439        # Test extended slicing by comparing with list slicing.
440        s = "".join(chr(c) for c in reversed(range(256)))
441        m = mmap.mmap(-1, len(s))
442        indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
443        for start in indices:
444            for stop in indices:
445                # Skip invalid step 0
446                for step in indices[1:]:
447                    m[:] = s
448                    self.assertEqual(m[:], s)
449                    L = list(s)
450                    # Make sure we have a slice of exactly the right length,
451                    # but with different data.
452                    data = L[start:stop:step]
453                    data = "".join(reversed(data))
454                    L[start:stop:step] = data
455                    m[start:stop:step] = data
456                    self.assertEqual(m[:], "".join(L))
457
458    def make_mmap_file (self, f, halfsize):
459        # Write 2 pages worth of data to the file
460        f.write ('\0' * halfsize)
461        f.write ('foo')
462        f.write ('\0' * (halfsize - 3))
463        f.flush ()
464        return mmap.mmap (f.fileno(), 0)
465
466    def test_empty_file (self):
467        f = open (TESTFN, 'w+b')
468        f.close()
469        with open(TESTFN, "rb") as f :
470            self.assertRaisesRegexp(ValueError,
471                                   "cannot mmap an empty file",
472                                   mmap.mmap, f.fileno(), 0,
473                                   access=mmap.ACCESS_READ)
474
475    def test_offset (self):
476        f = open (TESTFN, 'w+b')
477
478        try: # unlink TESTFN no matter what
479            halfsize = mmap.ALLOCATIONGRANULARITY
480            m = self.make_mmap_file (f, halfsize)
481            m.close ()
482            f.close ()
483
484            mapsize = halfsize * 2
485            # Try invalid offset
486            f = open(TESTFN, "r+b")
487            for offset in [-2, -1, None]:
488                try:
489                    m = mmap.mmap(f.fileno(), mapsize, offset=offset)
490                    self.assertEqual(0, 1)
491                except (ValueError, TypeError, OverflowError):
492                    pass
493                else:
494                    self.assertEqual(0, 0)
495            f.close()
496
497            # Try valid offset, hopefully 8192 works on all OSes
498            f = open(TESTFN, "r+b")
499            m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
500            self.assertEqual(m[0:3], 'foo')
501            f.close()
502
503            # Try resizing map
504            try:
505                m.resize(512)
506            except SystemError:
507                pass
508            else:
509                # resize() is supported
510                self.assertEqual(len(m), 512)
511                # Check that we can no longer seek beyond the new size.
512                self.assertRaises(ValueError, m.seek, 513, 0)
513                # Check that the content is not changed
514                self.assertEqual(m[0:3], 'foo')
515
516                # Check that the underlying file is truncated too
517                f = open(TESTFN)
518                f.seek(0, 2)
519                self.assertEqual(f.tell(), halfsize + 512)
520                f.close()
521                self.assertEqual(m.size(), halfsize + 512)
522
523            m.close()
524
525        finally:
526            f.close()
527            try:
528                os.unlink(TESTFN)
529            except OSError:
530                pass
531
532    def test_subclass(self):
533        class anon_mmap(mmap.mmap):
534            def __new__(klass, *args, **kwargs):
535                return mmap.mmap.__new__(klass, -1, *args, **kwargs)
536        anon_mmap(PAGESIZE)
537
538    @unittest.skipUnless(hasattr(mmap, 'PROT_READ'), "needs mmap.PROT_READ")
539    def test_prot_readonly(self):
540        mapsize = 10
541        open(TESTFN, "wb").write("a"*mapsize)
542        f = open(TESTFN, "rb")
543        m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
544        self.assertRaises(TypeError, m.write, "foo")
545        f.close()
546
547    def test_error(self):
548        self.assertTrue(issubclass(mmap.error, EnvironmentError))
549        self.assertIn("mmap.error", str(mmap.error))
550
551    def test_io_methods(self):
552        data = "0123456789"
553        open(TESTFN, "wb").write("x"*len(data))
554        f = open(TESTFN, "r+b")
555        m = mmap.mmap(f.fileno(), len(data))
556        f.close()
557        # Test write_byte()
558        for i in xrange(len(data)):
559            self.assertEqual(m.tell(), i)
560            m.write_byte(data[i])
561            self.assertEqual(m.tell(), i+1)
562        self.assertRaises(ValueError, m.write_byte, "x")
563        self.assertEqual(m[:], data)
564        # Test read_byte()
565        m.seek(0)
566        for i in xrange(len(data)):
567            self.assertEqual(m.tell(), i)
568            self.assertEqual(m.read_byte(), data[i])
569            self.assertEqual(m.tell(), i+1)
570        self.assertRaises(ValueError, m.read_byte)
571        # Test read()
572        m.seek(3)
573        self.assertEqual(m.read(3), "345")
574        self.assertEqual(m.tell(), 6)
575        # Test write()
576        m.seek(3)
577        m.write("bar")
578        self.assertEqual(m.tell(), 6)
579        self.assertEqual(m[:], "012bar6789")
580        m.seek(8)
581        self.assertRaises(ValueError, m.write, "bar")
582
583    @unittest.skipUnless(os.name == 'nt', 'requires Windows')
584    def test_tagname(self):
585        data1 = "0123456789"
586        data2 = "abcdefghij"
587        assert len(data1) == len(data2)
588
589        # Test same tag
590        m1 = mmap.mmap(-1, len(data1), tagname="foo")
591        m1[:] = data1
592        m2 = mmap.mmap(-1, len(data2), tagname="foo")
593        m2[:] = data2
594        self.assertEqual(m1[:], data2)
595        self.assertEqual(m2[:], data2)
596        m2.close()
597        m1.close()
598
599        # Test different tag
600        m1 = mmap.mmap(-1, len(data1), tagname="foo")
601        m1[:] = data1
602        m2 = mmap.mmap(-1, len(data2), tagname="boo")
603        m2[:] = data2
604        self.assertEqual(m1[:], data1)
605        self.assertEqual(m2[:], data2)
606        m2.close()
607        m1.close()
608
609    @cpython_only
610    @unittest.skipUnless(os.name == 'nt', 'requires Windows')
611    def test_sizeof(self):
612        m1 = mmap.mmap(-1, 100)
613        tagname = "foo"
614        m2 = mmap.mmap(-1, 100, tagname=tagname)
615        self.assertEqual(sys.getsizeof(m2),
616                         sys.getsizeof(m1) + len(tagname) + 1)
617
618    @unittest.skipUnless(os.name == 'nt', 'requires Windows')
619    def test_crasher_on_windows(self):
620        # Should not crash (Issue 1733986)
621        m = mmap.mmap(-1, 1000, tagname="foo")
622        try:
623            mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
624        except:
625            pass
626        m.close()
627
628        # Should not crash (Issue 5385)
629        open(TESTFN, "wb").write("x"*10)
630        f = open(TESTFN, "r+b")
631        m = mmap.mmap(f.fileno(), 0)
632        f.close()
633        try:
634            m.resize(0) # will raise WindowsError
635        except:
636            pass
637        try:
638            m[:]
639        except:
640            pass
641        m.close()
642
643    @unittest.skipUnless(os.name == 'nt', 'requires Windows')
644    def test_invalid_descriptor(self):
645        # socket file descriptors are valid, but out of range
646        # for _get_osfhandle, causing a crash when validating the
647        # parameters to _get_osfhandle.
648        s = socket.socket()
649        try:
650            with self.assertRaises(mmap.error):
651                m = mmap.mmap(s.fileno(), 10)
652        finally:
653            s.close()
654
655    @unittest.skipIf(os.name == 'nt', 'cannot resize anonymous mmaps on Windows')
656    def test_resize_past_pos(self):
657        m = mmap.mmap(-1, 8192)
658        self.addCleanup(m.close)
659        m.read(5000)
660        try:
661            m.resize(4096)
662        except SystemError:
663            self.skipTest("resizing not supported")
664        self.assertEqual(m.read(14), '')
665        self.assertRaises(ValueError, m.read_byte)
666        self.assertRaises(ValueError, m.write_byte, 'b')
667        self.assertRaises(ValueError, m.write, 'abc')
668
669    def test_concat_repeat_exception(self):
670        m = mmap.mmap(-1, 16)
671        with self.assertRaises(TypeError):
672            m + m
673        with self.assertRaises(TypeError):
674            m * 2
675
676
677class LargeMmapTests(unittest.TestCase):
678
679    def setUp(self):
680        unlink(TESTFN)
681
682    def tearDown(self):
683        unlink(TESTFN)
684
685    def _make_test_file(self, num_zeroes, tail):
686        if sys.platform[:3] == 'win' or sys.platform == 'darwin':
687            requires('largefile',
688                'test requires %s bytes and a long time to run' % str(0x180000000))
689        f = open(TESTFN, 'w+b')
690        try:
691            f.seek(num_zeroes)
692            f.write(tail)
693            f.flush()
694        except (IOError, OverflowError):
695            f.close()
696            raise unittest.SkipTest("filesystem does not have largefile support")
697        return f
698
699    def test_large_offset(self):
700        with self._make_test_file(0x14FFFFFFF, b" ") as f:
701            m = mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ)
702            try:
703                self.assertEqual(m[0xFFFFFFF], b" ")
704            finally:
705                m.close()
706
707    def test_large_filesize(self):
708        with self._make_test_file(0x17FFFFFFF, b" ") as f:
709            if sys.maxsize < 0x180000000:
710                # On 32 bit platforms the file is larger than sys.maxsize so
711                # mapping the whole file should fail -- Issue #16743
712                with self.assertRaises(OverflowError):
713                    mmap.mmap(f.fileno(), 0x180000000, access=mmap.ACCESS_READ)
714                with self.assertRaises(ValueError):
715                    mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
716            m = mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ)
717            try:
718                self.assertEqual(m.size(), 0x180000000)
719            finally:
720                m.close()
721
722    # Issue 11277: mmap() with large (~4GB) sparse files crashes on OS X.
723
724    def _test_around_boundary(self, boundary):
725        tail = b'  DEARdear  '
726        start = boundary - len(tail) // 2
727        end = start + len(tail)
728        with self._make_test_file(start, tail) as f:
729            m = mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
730            try:
731                self.assertEqual(m[start:end], tail)
732            finally:
733                m.close()
734
735    @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
736    def test_around_2GB(self):
737        self._test_around_boundary(_2G)
738
739    @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
740    def test_around_4GB(self):
741        self._test_around_boundary(_4G)
742
743
744def test_main():
745    run_unittest(MmapTests, LargeMmapTests)
746
747if __name__ == '__main__':
748    test_main()
749