blob: 899df8d8183368dfb31538775d845a3b05982632 [file] [log] [blame]
Nadeem Vawdaced10562011-05-07 13:01:50 +02001from test.support import (TESTFN, run_unittest, import_module, unlink,
2 requires, _2G, _4G)
Thomas Wouters89f507f2006-12-13 04:49:30 +00003import unittest
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +00004import os
5import re
6import itertools
7import socket
8import sys
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +00009
R. David Murraya21e4ca2009-03-31 23:16:50 +000010# Skip test if we can't import mmap.
11mmap = import_module('mmap')
12
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +000013PAGESIZE = mmap.PAGESIZE
14
Thomas Wouters89f507f2006-12-13 04:49:30 +000015class MmapTests(unittest.TestCase):
Fred Drake004d5e62000-10-23 17:22:08 +000016
Thomas Wouters89f507f2006-12-13 04:49:30 +000017 def setUp(self):
18 if os.path.exists(TESTFN):
19 os.unlink(TESTFN)
Fred Drake004d5e62000-10-23 17:22:08 +000020
Thomas Wouters89f507f2006-12-13 04:49:30 +000021 def tearDown(self):
Tim Petersfd692082001-05-10 20:03:04 +000022 try:
Fred Drake62787992001-05-11 14:29:21 +000023 os.unlink(TESTFN)
Tim Petersfd692082001-05-10 20:03:04 +000024 except OSError:
25 pass
26
Thomas Wouters89f507f2006-12-13 04:49:30 +000027 def test_basic(self):
28 # Test mmap module on Unix systems and Windows
29
30 # Create a file to be mmap'ed.
Guido van Rossumb358a2c2007-07-16 19:29:02 +000031 f = open(TESTFN, 'bw+')
Thomas Wouters89f507f2006-12-13 04:49:30 +000032 try:
33 # Write 2 pages worth of data to the file
Guido van Rossumb358a2c2007-07-16 19:29:02 +000034 f.write(b'\0'* PAGESIZE)
35 f.write(b'foo')
36 f.write(b'\0'* (PAGESIZE-3) )
Thomas Wouters89f507f2006-12-13 04:49:30 +000037 f.flush()
38 m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000039 finally:
Thomas Wouters89f507f2006-12-13 04:49:30 +000040 f.close()
41
Guido van Rossum456fe5d2007-07-16 19:42:05 +000042 # Simple sanity checks
Thomas Wouters89f507f2006-12-13 04:49:30 +000043
Guido van Rossum456fe5d2007-07-16 19:42:05 +000044 tp = str(type(m)) # SF bug 128713: segfaulted on Linux
Benjamin Petersone099b372009-04-04 17:09:35 +000045 self.assertEqual(m.find(b'foo'), PAGESIZE)
Thomas Wouters89f507f2006-12-13 04:49:30 +000046
Guido van Rossum456fe5d2007-07-16 19:42:05 +000047 self.assertEqual(len(m), 2*PAGESIZE)
Thomas Wouters89f507f2006-12-13 04:49:30 +000048
Guido van Rossum98297ee2007-11-06 21:34:58 +000049 self.assertEqual(m[0], 0)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000050 self.assertEqual(m[0:3], b'\0\0\0')
Thomas Wouters89f507f2006-12-13 04:49:30 +000051
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +000052 # Shouldn't crash on boundary (Issue #5292)
53 self.assertRaises(IndexError, m.__getitem__, len(m))
54 self.assertRaises(IndexError, m.__setitem__, len(m), b'\0')
55
Guido van Rossum456fe5d2007-07-16 19:42:05 +000056 # Modify the file's content
Guido van Rossum98297ee2007-11-06 21:34:58 +000057 m[0] = b'3'[0]
Guido van Rossum456fe5d2007-07-16 19:42:05 +000058 m[PAGESIZE +3: PAGESIZE +3+3] = b'bar'
Thomas Wouters89f507f2006-12-13 04:49:30 +000059
Guido van Rossum456fe5d2007-07-16 19:42:05 +000060 # Check that the modification worked
Guido van Rossum98297ee2007-11-06 21:34:58 +000061 self.assertEqual(m[0], b'3'[0])
Guido van Rossum456fe5d2007-07-16 19:42:05 +000062 self.assertEqual(m[0:3], b'3\0\0')
63 self.assertEqual(m[PAGESIZE-1 : PAGESIZE + 7], b'\0foobar\0')
Thomas Wouters89f507f2006-12-13 04:49:30 +000064
Guido van Rossum456fe5d2007-07-16 19:42:05 +000065 m.flush()
Thomas Wouters89f507f2006-12-13 04:49:30 +000066
Guido van Rossum456fe5d2007-07-16 19:42:05 +000067 # Test doing a regular expression match in an mmap'ed file
Antoine Pitroufd036452008-08-19 17:56:33 +000068 match = re.search(b'[A-Za-z]+', m)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000069 if match is None:
70 self.fail('regex match on mmap failed!')
71 else:
72 start, end = match.span(0)
73 length = end - start
Thomas Wouters89f507f2006-12-13 04:49:30 +000074
Guido van Rossum456fe5d2007-07-16 19:42:05 +000075 self.assertEqual(start, PAGESIZE)
76 self.assertEqual(end, PAGESIZE + 6)
Thomas Wouters89f507f2006-12-13 04:49:30 +000077
Guido van Rossum456fe5d2007-07-16 19:42:05 +000078 # test seeking around (try to overflow the seek implementation)
79 m.seek(0,0)
80 self.assertEqual(m.tell(), 0)
81 m.seek(42,1)
82 self.assertEqual(m.tell(), 42)
83 m.seek(0,2)
84 self.assertEqual(m.tell(), len(m))
Thomas Wouters89f507f2006-12-13 04:49:30 +000085
Guido van Rossum456fe5d2007-07-16 19:42:05 +000086 # Try to seek to negative position...
87 self.assertRaises(ValueError, m.seek, -1)
Thomas Wouters89f507f2006-12-13 04:49:30 +000088
Guido van Rossum456fe5d2007-07-16 19:42:05 +000089 # Try to seek beyond end of mmap...
90 self.assertRaises(ValueError, m.seek, 1, 2)
Thomas Wouters89f507f2006-12-13 04:49:30 +000091
Guido van Rossum456fe5d2007-07-16 19:42:05 +000092 # Try to seek to negative position...
93 self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
Thomas Wouters89f507f2006-12-13 04:49:30 +000094
Guido van Rossum456fe5d2007-07-16 19:42:05 +000095 # Try resizing map
96 try:
97 m.resize(512)
98 except SystemError:
99 # resize() not supported
100 # No messages are printed, since the output of this test suite
101 # would then be different across platforms.
102 pass
103 else:
104 # resize() is supported
105 self.assertEqual(len(m), 512)
106 # Check that we can no longer seek beyond the new size.
107 self.assertRaises(ValueError, m.seek, 513, 0)
108
109 # Check that the underlying file is truncated too
110 # (bug #728515)
Victor Stinnera6d2c762011-06-30 18:20:11 +0200111 f = open(TESTFN, 'rb')
Thomas Wouters89f507f2006-12-13 04:49:30 +0000112 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000113 f.seek(0, 2)
114 self.assertEqual(f.tell(), 512)
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000115 finally:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000116 f.close()
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000117 self.assertEqual(m.size(), 512)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000118
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000119 m.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000120
121 def test_access_parameter(self):
122 # Test for "access" keyword parameter
Tim Peters5ebfd362001-11-13 23:11:19 +0000123 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000124 with open(TESTFN, "wb") as fp:
125 fp.write(b"a"*mapsize)
126 with open(TESTFN, "rb") as f:
127 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_READ)
128 self.assertEqual(m[:], b'a'*mapsize, "Readonly memory map data incorrect.")
Tim Peters5ebfd362001-11-13 23:11:19 +0000129
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000130 # Ensuring that readonly mmap can't be slice assigned
131 try:
132 m[:] = b'b'*mapsize
133 except TypeError:
134 pass
135 else:
136 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000137
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000138 # Ensuring that readonly mmap can't be item assigned
139 try:
140 m[0] = b'b'
141 except TypeError:
142 pass
143 else:
144 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000145
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000146 # Ensuring that readonly mmap can't be write() to
147 try:
148 m.seek(0,0)
149 m.write(b'abc')
150 except TypeError:
151 pass
152 else:
153 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000154
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000155 # Ensuring that readonly mmap can't be write_byte() to
156 try:
157 m.seek(0,0)
158 m.write_byte(b'd')
159 except TypeError:
160 pass
161 else:
162 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000163
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000164 # Ensuring that readonly mmap can't be resized
165 try:
166 m.resize(2*mapsize)
167 except SystemError: # resize is not universally supported
168 pass
169 except TypeError:
170 pass
171 else:
172 self.fail("Able to resize readonly memory map")
173 with open(TESTFN, "rb") as fp:
174 self.assertEqual(fp.read(), b'a'*mapsize,
175 "Readonly memory map data file was modified")
Tim Peters5ebfd362001-11-13 23:11:19 +0000176
Thomas Wouters89f507f2006-12-13 04:49:30 +0000177 # Opening mmap with size too big
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000178 with open(TESTFN, "r+b") as f:
179 try:
180 m = mmap.mmap(f.fileno(), mapsize+1)
181 except ValueError:
182 # we do not expect a ValueError on Windows
183 # CAUTION: This also changes the size of the file on disk, and
184 # later tests assume that the length hasn't changed. We need to
185 # repair that.
186 if sys.platform.startswith('win'):
187 self.fail("Opening mmap with size+1 should work on Windows.")
188 else:
189 # we expect a ValueError on Unix, but not on Windows
190 if not sys.platform.startswith('win'):
191 self.fail("Opening mmap with size+1 should raise ValueError.")
192 m.close()
Neal Norwitzb5673922002-09-05 21:48:07 +0000193 if sys.platform.startswith('win'):
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000194 # Repair damage from the resizing test.
195 with open(TESTFN, 'r+b') as f:
196 f.truncate(mapsize)
Neal Norwitzb5673922002-09-05 21:48:07 +0000197
Thomas Wouters89f507f2006-12-13 04:49:30 +0000198 # Opening mmap with access=ACCESS_WRITE
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000199 with open(TESTFN, "r+b") as f:
200 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE)
201 # Modifying write-through memory map
202 m[:] = b'c'*mapsize
203 self.assertEqual(m[:], b'c'*mapsize,
204 "Write-through memory map memory not updated properly.")
205 m.flush()
206 m.close()
207 with open(TESTFN, 'rb') as f:
208 stuff = f.read()
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000209 self.assertEqual(stuff, b'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000210 "Write-through memory map data file not updated properly.")
211
Thomas Wouters89f507f2006-12-13 04:49:30 +0000212 # Opening mmap with access=ACCESS_COPY
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000213 with open(TESTFN, "r+b") as f:
214 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY)
215 # Modifying copy-on-write memory map
216 m[:] = b'd'*mapsize
217 self.assertEqual(m[:], b'd' * mapsize,
218 "Copy-on-write memory map data not written correctly.")
219 m.flush()
220 with open(TESTFN, "rb") as fp:
221 self.assertEqual(fp.read(), b'c'*mapsize,
222 "Copy-on-write test data file should not be modified.")
223 # Ensuring copy-on-write maps cannot be resized
224 self.assertRaises(TypeError, m.resize, 2*mapsize)
225 m.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000226
227 # Ensuring invalid access parameter raises exception
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000228 with open(TESTFN, "r+b") as f:
229 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4)
Tim Peters5ebfd362001-11-13 23:11:19 +0000230
231 if os.name == "posix":
Tim Peters00cafa02001-11-13 23:39:47 +0000232 # Try incompatible flags, prot and access parameters.
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000233 with open(TESTFN, "r+b") as f:
234 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize,
235 flags=mmap.MAP_PRIVATE,
236 prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE)
Tim Peters5ebfd362001-11-13 23:11:19 +0000237
Antoine Pitrou7b50c2c2011-03-06 01:47:18 +0100238 # Try writing with PROT_EXEC and without PROT_WRITE
239 prot = mmap.PROT_READ | getattr(mmap, 'PROT_EXEC', 0)
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +0100240 with open(TESTFN, "r+b") as f:
Antoine Pitrou7b50c2c2011-03-06 01:47:18 +0100241 m = mmap.mmap(f.fileno(), mapsize, prot=prot)
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +0100242 self.assertRaises(TypeError, m.write, b"abcdef")
243 self.assertRaises(TypeError, m.write_byte, 0)
244 m.close()
245
Thomas Wouters89f507f2006-12-13 04:49:30 +0000246 def test_bad_file_desc(self):
247 # Try opening a bad file descriptor...
248 self.assertRaises(mmap.error, mmap.mmap, -2, 4096)
Neal Norwitz3b4fff82006-01-11 08:54:45 +0000249
Thomas Wouters89f507f2006-12-13 04:49:30 +0000250 def test_tougher_find(self):
251 # Do a tougher .find() test. SF bug 515943 pointed out that, in 2.2,
252 # searching for data with embedded \0 bytes didn't work.
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000253 with open(TESTFN, 'wb+') as f:
Tim Petersc9ffa062002-03-08 05:43:32 +0000254
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000255 data = b'aabaac\x00deef\x00\x00aa\x00'
256 n = len(data)
257 f.write(data)
258 f.flush()
259 m = mmap.mmap(f.fileno(), n)
Tim Petersc9ffa062002-03-08 05:43:32 +0000260
261 for start in range(n+1):
262 for finish in range(start, n+1):
263 slice = data[start : finish]
Thomas Wouters89f507f2006-12-13 04:49:30 +0000264 self.assertEqual(m.find(slice), data.find(slice))
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000265 self.assertEqual(m.find(slice + b'x'), -1)
Tim Petersddc82ea2003-01-13 21:38:45 +0000266 m.close()
Tim Petersc9ffa062002-03-08 05:43:32 +0000267
Georg Brandlfceab5a2008-01-19 20:08:23 +0000268 def test_find_end(self):
269 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000270 f = open(TESTFN, 'wb+')
271 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000272 n = len(data)
273 f.write(data)
274 f.flush()
275 m = mmap.mmap(f.fileno(), n)
276 f.close()
277
Benjamin Petersone099b372009-04-04 17:09:35 +0000278 self.assertEqual(m.find(b'one'), 0)
279 self.assertEqual(m.find(b'ones'), 8)
280 self.assertEqual(m.find(b'one', 0, -1), 0)
281 self.assertEqual(m.find(b'one', 1), 8)
282 self.assertEqual(m.find(b'one', 1, -1), 8)
283 self.assertEqual(m.find(b'one', 1, -2), -1)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000284
285
286 def test_rfind(self):
287 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000288 f = open(TESTFN, 'wb+')
289 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000290 n = len(data)
291 f.write(data)
292 f.flush()
293 m = mmap.mmap(f.fileno(), n)
294 f.close()
295
Benjamin Petersone099b372009-04-04 17:09:35 +0000296 self.assertEqual(m.rfind(b'one'), 8)
297 self.assertEqual(m.rfind(b'one '), 0)
298 self.assertEqual(m.rfind(b'one', 0, -1), 8)
299 self.assertEqual(m.rfind(b'one', 0, -2), 0)
300 self.assertEqual(m.rfind(b'one', 1, -1), 8)
301 self.assertEqual(m.rfind(b'one', 1, -2), -1)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000302
303
Thomas Wouters89f507f2006-12-13 04:49:30 +0000304 def test_double_close(self):
305 # make sure a double close doesn't crash on Solaris (Bug# 665913)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000306 f = open(TESTFN, 'wb+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000307
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000308 f.write(2**16 * b'a') # Arbitrary character
Neal Norwitze604c022003-01-10 20:52:16 +0000309 f.close()
310
Victor Stinnera6d2c762011-06-30 18:20:11 +0200311 f = open(TESTFN, 'rb')
Tim Petersddc82ea2003-01-13 21:38:45 +0000312 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
Neal Norwitze604c022003-01-10 20:52:16 +0000313 mf.close()
314 mf.close()
315 f.close()
316
Serhiy Storchaka79080682013-11-03 21:31:18 +0200317 @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
Thomas Wouters89f507f2006-12-13 04:49:30 +0000318 def test_entire_file(self):
319 # test mapping of entire file by passing 0 for map length
Serhiy Storchaka79080682013-11-03 21:31:18 +0200320 f = open(TESTFN, "wb+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000321
Serhiy Storchaka79080682013-11-03 21:31:18 +0200322 f.write(2**16 * b'm') # Arbitrary character
323 f.close()
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000324
Serhiy Storchaka79080682013-11-03 21:31:18 +0200325 f = open(TESTFN, "rb+")
326 mf = mmap.mmap(f.fileno(), 0)
327 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
328 self.assertEqual(mf.read(2**16), 2**16 * b"m")
329 mf.close()
330 f.close()
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000331
Serhiy Storchaka79080682013-11-03 21:31:18 +0200332 @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
Antoine Pitrou85f46152011-01-15 16:17:07 +0000333 def test_length_0_offset(self):
334 # Issue #10916: test mapping of remainder of file by passing 0 for
335 # map length with an offset doesn't cause a segfault.
Antoine Pitroud0ebc752011-01-15 17:25:58 +0000336 # NOTE: allocation granularity is currently 65536 under Win64,
337 # and therefore the minimum offset alignment.
338 with open(TESTFN, "wb") as f:
339 f.write((65536 * 2) * b'm') # Arbitrary character
Antoine Pitrou85f46152011-01-15 16:17:07 +0000340
341 with open(TESTFN, "rb") as f:
Antoine Pitroud0ebc752011-01-15 17:25:58 +0000342 with mmap.mmap(f.fileno(), 0, offset=65536, access=mmap.ACCESS_READ) as mf:
343 self.assertRaises(IndexError, mf.__getitem__, 80000)
Antoine Pitrou85f46152011-01-15 16:17:07 +0000344
Serhiy Storchaka79080682013-11-03 21:31:18 +0200345 @unittest.skipUnless(hasattr(os, "stat"), "needs os.stat()")
Antoine Pitrou305bc9e2011-01-20 21:07:24 +0000346 def test_length_0_large_offset(self):
347 # Issue #10959: test mapping of a file by passing 0 for
348 # map length with a large offset doesn't cause a segfault.
Antoine Pitrou305bc9e2011-01-20 21:07:24 +0000349 with open(TESTFN, "wb") as f:
350 f.write(115699 * b'm') # Arbitrary character
351
352 with open(TESTFN, "w+b") as f:
353 self.assertRaises(ValueError, mmap.mmap, f.fileno(), 0,
354 offset=2147418112)
355
Thomas Wouters89f507f2006-12-13 04:49:30 +0000356 def test_move(self):
357 # make move works everywhere (64-bit format problem earlier)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000358 f = open(TESTFN, 'wb+')
Tim Peterseba28be2005-03-28 01:08:02 +0000359
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000360 f.write(b"ABCDEabcde") # Arbitrary character
Neal Norwitz8856fb72005-12-18 03:34:22 +0000361 f.flush()
362
363 mf = mmap.mmap(f.fileno(), 10)
364 mf.move(5, 0, 5)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000365 self.assertEqual(mf[:], b"ABCDEABCDE", "Map move should have duplicated front 5")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000366 mf.close()
367 f.close()
368
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000369 # more excessive test
370 data = b"0123456789"
371 for dest in range(len(data)):
372 for src in range(len(data)):
373 for count in range(len(data) - max(dest, src)):
374 expected = data[:dest] + data[src:src+count] + data[dest+count:]
375 m = mmap.mmap(-1, len(data))
376 m[:] = data
377 m.move(dest, src, count)
378 self.assertEqual(m[:], expected)
379 m.close()
380
Hirokazu Yamamoto2ca15012009-03-31 20:43:56 +0000381 # segfault test (Issue 5387)
382 m = mmap.mmap(-1, 100)
383 offsets = [-100, -1, 0, 1, 100]
384 for source, dest, size in itertools.product(offsets, offsets, offsets):
385 try:
386 m.move(source, dest, size)
387 except ValueError:
388 pass
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000389
390 offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
391 (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
392 for source, dest, size in offsets:
393 self.assertRaises(ValueError, m.move, source, dest, size)
394
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000395 m.close()
396
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000397 m = mmap.mmap(-1, 1) # single byte
398 self.assertRaises(ValueError, m.move, 0, 0, 2)
399 self.assertRaises(ValueError, m.move, 1, 0, 1)
400 self.assertRaises(ValueError, m.move, 0, 1, 1)
401 m.move(0, 0, 1)
402 m.move(0, 0, 0)
403
404
Thomas Wouters89f507f2006-12-13 04:49:30 +0000405 def test_anonymous(self):
406 # anonymous mmap.mmap(-1, PAGE)
407 m = mmap.mmap(-1, PAGESIZE)
Guido van Rossum805365e2007-05-07 22:24:25 +0000408 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000409 self.assertEqual(m[x], 0,
410 "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000411
Guido van Rossum805365e2007-05-07 22:24:25 +0000412 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000413 b = x & 0xff
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000414 m[x] = b
415 self.assertEqual(m[x], b)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000416
Charles-François Natali4dd453c2011-06-08 19:18:14 +0200417 def test_read_all(self):
418 m = mmap.mmap(-1, 16)
419 self.addCleanup(m.close)
420
421 # With no parameters, or None or a negative argument, reads all
422 m.write(bytes(range(16)))
423 m.seek(0)
424 self.assertEqual(m.read(), bytes(range(16)))
425 m.seek(8)
426 self.assertEqual(m.read(), bytes(range(8, 16)))
427 m.seek(16)
428 self.assertEqual(m.read(), b'')
429 m.seek(3)
430 self.assertEqual(m.read(None), bytes(range(3, 16)))
431 m.seek(4)
432 self.assertEqual(m.read(-1), bytes(range(4, 16)))
433 m.seek(5)
434 self.assertEqual(m.read(-2), bytes(range(5, 16)))
435 m.seek(9)
436 self.assertEqual(m.read(-42), bytes(range(9, 16)))
437
438 def test_read_invalid_arg(self):
439 m = mmap.mmap(-1, 16)
440 self.addCleanup(m.close)
441
442 self.assertRaises(TypeError, m.read, 'foo')
443 self.assertRaises(TypeError, m.read, 5.5)
444 self.assertRaises(TypeError, m.read, [1, 2, 3])
445
Thomas Woutersed03b412007-08-28 21:37:11 +0000446 def test_extended_getslice(self):
447 # Test extended slicing by comparing with list slicing.
448 s = bytes(reversed(range(256)))
449 m = mmap.mmap(-1, len(s))
450 m[:] = s
451 self.assertEqual(m[:], s)
452 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
453 for start in indices:
454 for stop in indices:
455 # Skip step 0 (invalid)
456 for step in indices[1:]:
457 self.assertEqual(m[start:stop:step],
458 s[start:stop:step])
459
460 def test_extended_set_del_slice(self):
461 # Test extended slicing by comparing with list slicing.
462 s = bytes(reversed(range(256)))
463 m = mmap.mmap(-1, len(s))
464 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
465 for start in indices:
466 for stop in indices:
467 # Skip invalid step 0
468 for step in indices[1:]:
469 m[:] = s
470 self.assertEqual(m[:], s)
471 L = list(s)
472 # Make sure we have a slice of exactly the right length,
473 # but with different data.
474 data = L[start:stop:step]
475 data = bytes(reversed(data))
476 L[start:stop:step] = data
477 m[start:stop:step] = data
Ezio Melottib3aedd42010-11-20 19:04:17 +0000478 self.assertEqual(m[:], bytes(L))
Thomas Woutersed03b412007-08-28 21:37:11 +0000479
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000480 def make_mmap_file (self, f, halfsize):
481 # Write 2 pages worth of data to the file
482 f.write (b'\0' * halfsize)
483 f.write (b'foo')
484 f.write (b'\0' * (halfsize - 3))
485 f.flush ()
486 return mmap.mmap (f.fileno(), 0)
487
Jesus Cea941bfcc2012-09-10 00:27:55 +0200488 def test_empty_file (self):
489 f = open (TESTFN, 'w+b')
490 f.close()
Jesus Cea1f2799b2012-09-10 22:49:50 +0200491 with open(TESTFN, "rb") as f :
492 self.assertRaisesRegex(ValueError,
493 "cannot mmap an empty file",
494 mmap.mmap, f.fileno(), 0,
495 access=mmap.ACCESS_READ)
Jesus Cea941bfcc2012-09-10 00:27:55 +0200496
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000497 def test_offset (self):
498 f = open (TESTFN, 'w+b')
499
500 try: # unlink TESTFN no matter what
501 halfsize = mmap.ALLOCATIONGRANULARITY
502 m = self.make_mmap_file (f, halfsize)
503 m.close ()
504 f.close ()
505
506 mapsize = halfsize * 2
507 # Try invalid offset
508 f = open(TESTFN, "r+b")
509 for offset in [-2, -1, None]:
510 try:
511 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
512 self.assertEqual(0, 1)
513 except (ValueError, TypeError, OverflowError):
514 pass
515 else:
516 self.assertEqual(0, 0)
517 f.close()
518
519 # Try valid offset, hopefully 8192 works on all OSes
520 f = open(TESTFN, "r+b")
521 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
522 self.assertEqual(m[0:3], b'foo')
523 f.close()
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000524
525 # Try resizing map
526 try:
527 m.resize(512)
528 except SystemError:
529 pass
530 else:
531 # resize() is supported
532 self.assertEqual(len(m), 512)
533 # Check that we can no longer seek beyond the new size.
534 self.assertRaises(ValueError, m.seek, 513, 0)
535 # Check that the content is not changed
536 self.assertEqual(m[0:3], b'foo')
537
538 # Check that the underlying file is truncated too
Victor Stinnera6d2c762011-06-30 18:20:11 +0200539 f = open(TESTFN, 'rb')
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000540 f.seek(0, 2)
541 self.assertEqual(f.tell(), halfsize + 512)
542 f.close()
543 self.assertEqual(m.size(), halfsize + 512)
544
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000545 m.close()
546
547 finally:
548 f.close()
549 try:
550 os.unlink(TESTFN)
551 except OSError:
552 pass
553
Christian Heimes1af737c2008-01-23 08:24:23 +0000554 def test_subclass(self):
555 class anon_mmap(mmap.mmap):
556 def __new__(klass, *args, **kwargs):
557 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
558 anon_mmap(PAGESIZE)
559
Serhiy Storchaka79080682013-11-03 21:31:18 +0200560 @unittest.skipUnless(hasattr(mmap, 'PROT_READ'), "needs mmap.PROT_READ")
Christian Heimesa156e092008-02-16 07:38:31 +0000561 def test_prot_readonly(self):
562 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000563 with open(TESTFN, "wb") as fp:
564 fp.write(b"a"*mapsize)
Christian Heimesa156e092008-02-16 07:38:31 +0000565 f = open(TESTFN, "rb")
566 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
567 self.assertRaises(TypeError, m.write, "foo")
Martin v. Löwise1e9f232008-04-01 06:17:46 +0000568 f.close()
Christian Heimes1af737c2008-01-23 08:24:23 +0000569
Christian Heimes7131fd92008-02-19 14:21:46 +0000570 def test_error(self):
Antoine Pitrou6b4883d2011-10-12 02:54:14 +0200571 self.assertIs(mmap.error, OSError)
Christian Heimes7131fd92008-02-19 14:21:46 +0000572
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000573 def test_io_methods(self):
574 data = b"0123456789"
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000575 with open(TESTFN, "wb") as fp:
576 fp.write(b"x"*len(data))
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000577 f = open(TESTFN, "r+b")
578 m = mmap.mmap(f.fileno(), len(data))
579 f.close()
580 # Test write_byte()
581 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000582 self.assertEqual(m.tell(), i)
Benjamin Petersone099b372009-04-04 17:09:35 +0000583 m.write_byte(data[i])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000584 self.assertEqual(m.tell(), i+1)
Benjamin Petersone099b372009-04-04 17:09:35 +0000585 self.assertRaises(ValueError, m.write_byte, b"x"[0])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000586 self.assertEqual(m[:], data)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000587 # Test read_byte()
588 m.seek(0)
589 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000590 self.assertEqual(m.tell(), i)
591 self.assertEqual(m.read_byte(), data[i])
592 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000593 self.assertRaises(ValueError, m.read_byte)
594 # Test read()
595 m.seek(3)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000596 self.assertEqual(m.read(3), b"345")
597 self.assertEqual(m.tell(), 6)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000598 # Test write()
599 m.seek(3)
600 m.write(b"bar")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000601 self.assertEqual(m.tell(), 6)
602 self.assertEqual(m[:], b"012bar6789")
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000603 m.seek(8)
604 self.assertRaises(ValueError, m.write, b"bar")
605
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000606 def test_non_ascii_byte(self):
607 for b in (129, 200, 255): # > 128
608 m = mmap.mmap(-1, 1)
609 m.write_byte(b)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000610 self.assertEqual(m[0], b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000611 m.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000612 self.assertEqual(m.read_byte(), b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000613 m.close()
614
Serhiy Storchaka79080682013-11-03 21:31:18 +0200615 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
616 def test_tagname(self):
617 data1 = b"0123456789"
618 data2 = b"abcdefghij"
619 assert len(data1) == len(data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000620
Serhiy Storchaka79080682013-11-03 21:31:18 +0200621 # Test same tag
622 m1 = mmap.mmap(-1, len(data1), tagname="foo")
623 m1[:] = data1
624 m2 = mmap.mmap(-1, len(data2), tagname="foo")
625 m2[:] = data2
626 self.assertEqual(m1[:], data2)
627 self.assertEqual(m2[:], data2)
628 m2.close()
629 m1.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000630
Serhiy Storchaka79080682013-11-03 21:31:18 +0200631 # Test different tag
632 m1 = mmap.mmap(-1, len(data1), tagname="foo")
633 m1[:] = data1
634 m2 = mmap.mmap(-1, len(data2), tagname="boo")
635 m2[:] = data2
636 self.assertEqual(m1[:], data1)
637 self.assertEqual(m2[:], data2)
638 m2.close()
639 m1.close()
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000640
Serhiy Storchaka79080682013-11-03 21:31:18 +0200641 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
642 def test_crasher_on_windows(self):
643 # Should not crash (Issue 1733986)
644 m = mmap.mmap(-1, 1000, tagname="foo")
645 try:
646 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
647 except:
648 pass
649 m.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000650
Serhiy Storchaka79080682013-11-03 21:31:18 +0200651 # Should not crash (Issue 5385)
652 with open(TESTFN, "wb") as fp:
653 fp.write(b"x"*10)
654 f = open(TESTFN, "r+b")
655 m = mmap.mmap(f.fileno(), 0)
656 f.close()
657 try:
658 m.resize(0) # will raise WindowsError
659 except:
660 pass
661 try:
662 m[:]
663 except:
664 pass
665 m.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000666
Serhiy Storchaka79080682013-11-03 21:31:18 +0200667 @unittest.skipUnless(os.name == 'nt', 'requires Windows')
668 def test_invalid_descriptor(self):
669 # socket file descriptors are valid, but out of range
670 # for _get_osfhandle, causing a crash when validating the
671 # parameters to _get_osfhandle.
672 s = socket.socket()
673 try:
674 with self.assertRaises(mmap.error):
675 m = mmap.mmap(s.fileno(), 10)
676 finally:
677 s.close()
Brian Curtinea47eaa2010-08-01 15:26:26 +0000678
Georg Brandl0bccc182010-08-01 14:50:00 +0000679 def test_context_manager(self):
680 with mmap.mmap(-1, 10) as m:
681 self.assertFalse(m.closed)
682 self.assertTrue(m.closed)
683
684 def test_context_manager_exception(self):
685 # Test that the IOError gets passed through
686 with self.assertRaises(Exception) as exc:
687 with mmap.mmap(-1, 10) as m:
688 raise IOError
689 self.assertIsInstance(exc.exception, IOError,
690 "wrong exception raised in context manager")
691 self.assertTrue(m.closed, "context manager failed")
692
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000693class LargeMmapTests(unittest.TestCase):
694
695 def setUp(self):
696 unlink(TESTFN)
697
698 def tearDown(self):
699 unlink(TESTFN)
700
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200701 def _make_test_file(self, num_zeroes, tail):
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000702 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
703 requires('largefile',
704 'test requires %s bytes and a long time to run' % str(0x180000000))
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200705 f = open(TESTFN, 'w+b')
706 try:
707 f.seek(num_zeroes)
708 f.write(tail)
709 f.flush()
710 except (IOError, OverflowError):
Nadeem Vawda7420b702011-05-07 14:35:05 +0200711 f.close()
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200712 raise unittest.SkipTest("filesystem does not have largefile support")
713 return f
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000714
Nadeem Vawdaced10562011-05-07 13:01:50 +0200715 def test_large_offset(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200716 with self._make_test_file(0x14FFFFFFF, b" ") as f:
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000717 with mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ) as m:
718 self.assertEqual(m[0xFFFFFFF], 32)
719
720 def test_large_filesize(self):
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200721 with self._make_test_file(0x17FFFFFFF, b" ") as f:
Richard Oudkerk0d09ba82013-02-13 12:18:03 +0000722 if sys.maxsize < 0x180000000:
723 # On 32 bit platforms the file is larger than sys.maxsize so
724 # mapping the whole file should fail -- Issue #16743
725 with self.assertRaises(OverflowError):
726 mmap.mmap(f.fileno(), 0x180000000, access=mmap.ACCESS_READ)
727 with self.assertRaises(ValueError):
728 mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ)
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000729 with mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ) as m:
730 self.assertEqual(m.size(), 0x180000000)
731
Nadeem Vawdaced10562011-05-07 13:01:50 +0200732 # Issue 11277: mmap() with large (~4GB) sparse files crashes on OS X.
733
734 def _test_around_boundary(self, boundary):
735 tail = b' DEARdear '
736 start = boundary - len(tail) // 2
737 end = start + len(tail)
Nadeem Vawda909f6d22011-05-07 14:14:53 +0200738 with self._make_test_file(start, tail) as f:
Nadeem Vawdac2bb0732011-05-07 13:08:54 +0200739 with mmap.mmap(f.fileno(), 0, access=mmap.ACCESS_READ) as m:
Nadeem Vawdaced10562011-05-07 13:01:50 +0200740 self.assertEqual(m[start:end], tail)
Nadeem Vawdaced10562011-05-07 13:01:50 +0200741
742 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
743 def test_around_2GB(self):
744 self._test_around_boundary(_2G)
745
746 @unittest.skipUnless(sys.maxsize > _4G, "test cannot run on 32-bit systems")
747 def test_around_4GB(self):
748 self._test_around_boundary(_4G)
749
Christian Heimes7131fd92008-02-19 14:21:46 +0000750
Thomas Wouters89f507f2006-12-13 04:49:30 +0000751def test_main():
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000752 run_unittest(MmapTests, LargeMmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000753
Thomas Wouters89f507f2006-12-13 04:49:30 +0000754if __name__ == '__main__':
755 test_main()