blob: e62a046c0112f0d07cfa3e9e4d8f2d097bb0e955 [file] [log] [blame]
Antoine Pitrou97696cb2011-02-21 23:46:27 +00001from test.support import TESTFN, run_unittest, import_module, unlink, requires
Thomas Wouters89f507f2006-12-13 04:49:30 +00002import unittest
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +00003import os
4import re
5import itertools
6import socket
7import sys
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +00008
R. David Murraya21e4ca2009-03-31 23:16:50 +00009# Skip test if we can't import mmap.
10mmap = import_module('mmap')
11
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +000012PAGESIZE = mmap.PAGESIZE
13
Thomas Wouters89f507f2006-12-13 04:49:30 +000014class MmapTests(unittest.TestCase):
Fred Drake004d5e62000-10-23 17:22:08 +000015
Thomas Wouters89f507f2006-12-13 04:49:30 +000016 def setUp(self):
17 if os.path.exists(TESTFN):
18 os.unlink(TESTFN)
Fred Drake004d5e62000-10-23 17:22:08 +000019
Thomas Wouters89f507f2006-12-13 04:49:30 +000020 def tearDown(self):
Tim Petersfd692082001-05-10 20:03:04 +000021 try:
Fred Drake62787992001-05-11 14:29:21 +000022 os.unlink(TESTFN)
Tim Petersfd692082001-05-10 20:03:04 +000023 except OSError:
24 pass
25
Thomas Wouters89f507f2006-12-13 04:49:30 +000026 def test_basic(self):
27 # Test mmap module on Unix systems and Windows
28
29 # Create a file to be mmap'ed.
Guido van Rossumb358a2c2007-07-16 19:29:02 +000030 f = open(TESTFN, 'bw+')
Thomas Wouters89f507f2006-12-13 04:49:30 +000031 try:
32 # Write 2 pages worth of data to the file
Guido van Rossumb358a2c2007-07-16 19:29:02 +000033 f.write(b'\0'* PAGESIZE)
34 f.write(b'foo')
35 f.write(b'\0'* (PAGESIZE-3) )
Thomas Wouters89f507f2006-12-13 04:49:30 +000036 f.flush()
37 m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000038 finally:
Thomas Wouters89f507f2006-12-13 04:49:30 +000039 f.close()
40
Guido van Rossum456fe5d2007-07-16 19:42:05 +000041 # Simple sanity checks
Thomas Wouters89f507f2006-12-13 04:49:30 +000042
Guido van Rossum456fe5d2007-07-16 19:42:05 +000043 tp = str(type(m)) # SF bug 128713: segfaulted on Linux
Benjamin Petersone099b372009-04-04 17:09:35 +000044 self.assertEqual(m.find(b'foo'), PAGESIZE)
Thomas Wouters89f507f2006-12-13 04:49:30 +000045
Guido van Rossum456fe5d2007-07-16 19:42:05 +000046 self.assertEqual(len(m), 2*PAGESIZE)
Thomas Wouters89f507f2006-12-13 04:49:30 +000047
Guido van Rossum98297ee2007-11-06 21:34:58 +000048 self.assertEqual(m[0], 0)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000049 self.assertEqual(m[0:3], b'\0\0\0')
Thomas Wouters89f507f2006-12-13 04:49:30 +000050
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +000051 # Shouldn't crash on boundary (Issue #5292)
52 self.assertRaises(IndexError, m.__getitem__, len(m))
53 self.assertRaises(IndexError, m.__setitem__, len(m), b'\0')
54
Guido van Rossum456fe5d2007-07-16 19:42:05 +000055 # Modify the file's content
Guido van Rossum98297ee2007-11-06 21:34:58 +000056 m[0] = b'3'[0]
Guido van Rossum456fe5d2007-07-16 19:42:05 +000057 m[PAGESIZE +3: PAGESIZE +3+3] = b'bar'
Thomas Wouters89f507f2006-12-13 04:49:30 +000058
Guido van Rossum456fe5d2007-07-16 19:42:05 +000059 # Check that the modification worked
Guido van Rossum98297ee2007-11-06 21:34:58 +000060 self.assertEqual(m[0], b'3'[0])
Guido van Rossum456fe5d2007-07-16 19:42:05 +000061 self.assertEqual(m[0:3], b'3\0\0')
62 self.assertEqual(m[PAGESIZE-1 : PAGESIZE + 7], b'\0foobar\0')
Thomas Wouters89f507f2006-12-13 04:49:30 +000063
Guido van Rossum456fe5d2007-07-16 19:42:05 +000064 m.flush()
Thomas Wouters89f507f2006-12-13 04:49:30 +000065
Guido van Rossum456fe5d2007-07-16 19:42:05 +000066 # Test doing a regular expression match in an mmap'ed file
Antoine Pitroufd036452008-08-19 17:56:33 +000067 match = re.search(b'[A-Za-z]+', m)
Guido van Rossum456fe5d2007-07-16 19:42:05 +000068 if match is None:
69 self.fail('regex match on mmap failed!')
70 else:
71 start, end = match.span(0)
72 length = end - start
Thomas Wouters89f507f2006-12-13 04:49:30 +000073
Guido van Rossum456fe5d2007-07-16 19:42:05 +000074 self.assertEqual(start, PAGESIZE)
75 self.assertEqual(end, PAGESIZE + 6)
Thomas Wouters89f507f2006-12-13 04:49:30 +000076
Guido van Rossum456fe5d2007-07-16 19:42:05 +000077 # test seeking around (try to overflow the seek implementation)
78 m.seek(0,0)
79 self.assertEqual(m.tell(), 0)
80 m.seek(42,1)
81 self.assertEqual(m.tell(), 42)
82 m.seek(0,2)
83 self.assertEqual(m.tell(), len(m))
Thomas Wouters89f507f2006-12-13 04:49:30 +000084
Guido van Rossum456fe5d2007-07-16 19:42:05 +000085 # Try to seek to negative position...
86 self.assertRaises(ValueError, m.seek, -1)
Thomas Wouters89f507f2006-12-13 04:49:30 +000087
Guido van Rossum456fe5d2007-07-16 19:42:05 +000088 # Try to seek beyond end of mmap...
89 self.assertRaises(ValueError, m.seek, 1, 2)
Thomas Wouters89f507f2006-12-13 04:49:30 +000090
Guido van Rossum456fe5d2007-07-16 19:42:05 +000091 # Try to seek to negative position...
92 self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
Thomas Wouters89f507f2006-12-13 04:49:30 +000093
Guido van Rossum456fe5d2007-07-16 19:42:05 +000094 # Try resizing map
95 try:
96 m.resize(512)
97 except SystemError:
98 # resize() not supported
99 # No messages are printed, since the output of this test suite
100 # would then be different across platforms.
101 pass
102 else:
103 # resize() is supported
104 self.assertEqual(len(m), 512)
105 # Check that we can no longer seek beyond the new size.
106 self.assertRaises(ValueError, m.seek, 513, 0)
107
108 # Check that the underlying file is truncated too
109 # (bug #728515)
110 f = open(TESTFN)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000111 try:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000112 f.seek(0, 2)
113 self.assertEqual(f.tell(), 512)
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000114 finally:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000115 f.close()
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000116 self.assertEqual(m.size(), 512)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000117
Guido van Rossum456fe5d2007-07-16 19:42:05 +0000118 m.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000119
120 def test_access_parameter(self):
121 # Test for "access" keyword parameter
Tim Peters5ebfd362001-11-13 23:11:19 +0000122 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000123 with open(TESTFN, "wb") as fp:
124 fp.write(b"a"*mapsize)
125 with open(TESTFN, "rb") as f:
126 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_READ)
127 self.assertEqual(m[:], b'a'*mapsize, "Readonly memory map data incorrect.")
Tim Peters5ebfd362001-11-13 23:11:19 +0000128
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000129 # Ensuring that readonly mmap can't be slice assigned
130 try:
131 m[:] = b'b'*mapsize
132 except TypeError:
133 pass
134 else:
135 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000136
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000137 # Ensuring that readonly mmap can't be item assigned
138 try:
139 m[0] = b'b'
140 except TypeError:
141 pass
142 else:
143 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000144
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000145 # Ensuring that readonly mmap can't be write() to
146 try:
147 m.seek(0,0)
148 m.write(b'abc')
149 except TypeError:
150 pass
151 else:
152 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000153
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000154 # Ensuring that readonly mmap can't be write_byte() to
155 try:
156 m.seek(0,0)
157 m.write_byte(b'd')
158 except TypeError:
159 pass
160 else:
161 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000162
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000163 # Ensuring that readonly mmap can't be resized
164 try:
165 m.resize(2*mapsize)
166 except SystemError: # resize is not universally supported
167 pass
168 except TypeError:
169 pass
170 else:
171 self.fail("Able to resize readonly memory map")
172 with open(TESTFN, "rb") as fp:
173 self.assertEqual(fp.read(), b'a'*mapsize,
174 "Readonly memory map data file was modified")
Tim Peters5ebfd362001-11-13 23:11:19 +0000175
Thomas Wouters89f507f2006-12-13 04:49:30 +0000176 # Opening mmap with size too big
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000177 with open(TESTFN, "r+b") as f:
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()
Neal Norwitzb5673922002-09-05 21:48:07 +0000192 if sys.platform.startswith('win'):
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000193 # Repair damage from the resizing test.
194 with open(TESTFN, 'r+b') as f:
195 f.truncate(mapsize)
Neal Norwitzb5673922002-09-05 21:48:07 +0000196
Thomas Wouters89f507f2006-12-13 04:49:30 +0000197 # Opening mmap with access=ACCESS_WRITE
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000198 with open(TESTFN, "r+b") as f:
199 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE)
200 # Modifying write-through memory map
201 m[:] = b'c'*mapsize
202 self.assertEqual(m[:], b'c'*mapsize,
203 "Write-through memory map memory not updated properly.")
204 m.flush()
205 m.close()
206 with open(TESTFN, 'rb') as f:
207 stuff = f.read()
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000208 self.assertEqual(stuff, b'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000209 "Write-through memory map data file not updated properly.")
210
Thomas Wouters89f507f2006-12-13 04:49:30 +0000211 # Opening mmap with access=ACCESS_COPY
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000212 with open(TESTFN, "r+b") as f:
213 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY)
214 # Modifying copy-on-write memory map
215 m[:] = b'd'*mapsize
216 self.assertEqual(m[:], b'd' * mapsize,
217 "Copy-on-write memory map data not written correctly.")
218 m.flush()
219 with open(TESTFN, "rb") as fp:
220 self.assertEqual(fp.read(), b'c'*mapsize,
221 "Copy-on-write test data file should not be modified.")
222 # Ensuring copy-on-write maps cannot be resized
223 self.assertRaises(TypeError, m.resize, 2*mapsize)
224 m.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000225
226 # Ensuring invalid access parameter raises exception
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000227 with open(TESTFN, "r+b") as f:
228 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4)
Tim Peters5ebfd362001-11-13 23:11:19 +0000229
230 if os.name == "posix":
Tim Peters00cafa02001-11-13 23:39:47 +0000231 # Try incompatible flags, prot and access parameters.
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000232 with open(TESTFN, "r+b") as f:
233 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize,
234 flags=mmap.MAP_PRIVATE,
235 prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE)
Tim Peters5ebfd362001-11-13 23:11:19 +0000236
Antoine Pitrou7b50c2c2011-03-06 01:47:18 +0100237 # Try writing with PROT_EXEC and without PROT_WRITE
238 prot = mmap.PROT_READ | getattr(mmap, 'PROT_EXEC', 0)
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +0100239 with open(TESTFN, "r+b") as f:
Antoine Pitrou7b50c2c2011-03-06 01:47:18 +0100240 m = mmap.mmap(f.fileno(), mapsize, prot=prot)
Antoine Pitrou16a0a0b2011-03-06 01:11:03 +0100241 self.assertRaises(TypeError, m.write, b"abcdef")
242 self.assertRaises(TypeError, m.write_byte, 0)
243 m.close()
244
Thomas Wouters89f507f2006-12-13 04:49:30 +0000245 def test_bad_file_desc(self):
246 # Try opening a bad file descriptor...
247 self.assertRaises(mmap.error, mmap.mmap, -2, 4096)
Neal Norwitz3b4fff82006-01-11 08:54:45 +0000248
Thomas Wouters89f507f2006-12-13 04:49:30 +0000249 def test_tougher_find(self):
250 # Do a tougher .find() test. SF bug 515943 pointed out that, in 2.2,
251 # searching for data with embedded \0 bytes didn't work.
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000252 with open(TESTFN, 'wb+') as f:
Tim Petersc9ffa062002-03-08 05:43:32 +0000253
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000254 data = b'aabaac\x00deef\x00\x00aa\x00'
255 n = len(data)
256 f.write(data)
257 f.flush()
258 m = mmap.mmap(f.fileno(), n)
Tim Petersc9ffa062002-03-08 05:43:32 +0000259
260 for start in range(n+1):
261 for finish in range(start, n+1):
262 slice = data[start : finish]
Thomas Wouters89f507f2006-12-13 04:49:30 +0000263 self.assertEqual(m.find(slice), data.find(slice))
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000264 self.assertEqual(m.find(slice + b'x'), -1)
Tim Petersddc82ea2003-01-13 21:38:45 +0000265 m.close()
Tim Petersc9ffa062002-03-08 05:43:32 +0000266
Georg Brandlfceab5a2008-01-19 20:08:23 +0000267 def test_find_end(self):
268 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000269 f = open(TESTFN, 'wb+')
270 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000271 n = len(data)
272 f.write(data)
273 f.flush()
274 m = mmap.mmap(f.fileno(), n)
275 f.close()
276
Benjamin Petersone099b372009-04-04 17:09:35 +0000277 self.assertEqual(m.find(b'one'), 0)
278 self.assertEqual(m.find(b'ones'), 8)
279 self.assertEqual(m.find(b'one', 0, -1), 0)
280 self.assertEqual(m.find(b'one', 1), 8)
281 self.assertEqual(m.find(b'one', 1, -1), 8)
282 self.assertEqual(m.find(b'one', 1, -2), -1)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000283
284
285 def test_rfind(self):
286 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000287 f = open(TESTFN, 'wb+')
288 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000289 n = len(data)
290 f.write(data)
291 f.flush()
292 m = mmap.mmap(f.fileno(), n)
293 f.close()
294
Benjamin Petersone099b372009-04-04 17:09:35 +0000295 self.assertEqual(m.rfind(b'one'), 8)
296 self.assertEqual(m.rfind(b'one '), 0)
297 self.assertEqual(m.rfind(b'one', 0, -1), 8)
298 self.assertEqual(m.rfind(b'one', 0, -2), 0)
299 self.assertEqual(m.rfind(b'one', 1, -1), 8)
300 self.assertEqual(m.rfind(b'one', 1, -2), -1)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000301
302
Thomas Wouters89f507f2006-12-13 04:49:30 +0000303 def test_double_close(self):
304 # make sure a double close doesn't crash on Solaris (Bug# 665913)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000305 f = open(TESTFN, 'wb+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000306
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000307 f.write(2**16 * b'a') # Arbitrary character
Neal Norwitze604c022003-01-10 20:52:16 +0000308 f.close()
309
310 f = open(TESTFN)
Tim Petersddc82ea2003-01-13 21:38:45 +0000311 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
Neal Norwitze604c022003-01-10 20:52:16 +0000312 mf.close()
313 mf.close()
314 f.close()
315
Thomas Wouters89f507f2006-12-13 04:49:30 +0000316 def test_entire_file(self):
317 # test mapping of entire file by passing 0 for map length
318 if hasattr(os, "stat"):
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000319 f = open(TESTFN, "wb+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000320
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000321 f.write(2**16 * b'm') # Arbitrary character
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000322 f.close()
323
324 f = open(TESTFN, "rb+")
Tim Peterseba28be2005-03-28 01:08:02 +0000325 mf = mmap.mmap(f.fileno(), 0)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000326 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000327 self.assertEqual(mf.read(2**16), 2**16 * b"m")
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000328 mf.close()
329 f.close()
330
Antoine Pitrou85f46152011-01-15 16:17:07 +0000331 def test_length_0_offset(self):
332 # Issue #10916: test mapping of remainder of file by passing 0 for
333 # map length with an offset doesn't cause a segfault.
334 if not hasattr(os, "stat"):
335 self.skipTest("needs os.stat")
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
Antoine Pitrou305bc9e2011-01-20 21:07:24 +0000345 def test_length_0_large_offset(self):
346 # Issue #10959: test mapping of a file by passing 0 for
347 # map length with a large offset doesn't cause a segfault.
348 if not hasattr(os, "stat"):
349 self.skipTest("needs os.stat")
350
351 with open(TESTFN, "wb") as f:
352 f.write(115699 * b'm') # Arbitrary character
353
354 with open(TESTFN, "w+b") as f:
355 self.assertRaises(ValueError, mmap.mmap, f.fileno(), 0,
356 offset=2147418112)
357
Thomas Wouters89f507f2006-12-13 04:49:30 +0000358 def test_move(self):
359 # make move works everywhere (64-bit format problem earlier)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000360 f = open(TESTFN, 'wb+')
Tim Peterseba28be2005-03-28 01:08:02 +0000361
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000362 f.write(b"ABCDEabcde") # Arbitrary character
Neal Norwitz8856fb72005-12-18 03:34:22 +0000363 f.flush()
364
365 mf = mmap.mmap(f.fileno(), 10)
366 mf.move(5, 0, 5)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000367 self.assertEqual(mf[:], b"ABCDEABCDE", "Map move should have duplicated front 5")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000368 mf.close()
369 f.close()
370
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000371 # more excessive test
372 data = b"0123456789"
373 for dest in range(len(data)):
374 for src in range(len(data)):
375 for count in range(len(data) - max(dest, src)):
376 expected = data[:dest] + data[src:src+count] + data[dest+count:]
377 m = mmap.mmap(-1, len(data))
378 m[:] = data
379 m.move(dest, src, count)
380 self.assertEqual(m[:], expected)
381 m.close()
382
Hirokazu Yamamoto2ca15012009-03-31 20:43:56 +0000383 # segfault test (Issue 5387)
384 m = mmap.mmap(-1, 100)
385 offsets = [-100, -1, 0, 1, 100]
386 for source, dest, size in itertools.product(offsets, offsets, offsets):
387 try:
388 m.move(source, dest, size)
389 except ValueError:
390 pass
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000391
392 offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
393 (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
394 for source, dest, size in offsets:
395 self.assertRaises(ValueError, m.move, source, dest, size)
396
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000397 m.close()
398
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000399 m = mmap.mmap(-1, 1) # single byte
400 self.assertRaises(ValueError, m.move, 0, 0, 2)
401 self.assertRaises(ValueError, m.move, 1, 0, 1)
402 self.assertRaises(ValueError, m.move, 0, 1, 1)
403 m.move(0, 0, 1)
404 m.move(0, 0, 0)
405
406
Thomas Wouters89f507f2006-12-13 04:49:30 +0000407 def test_anonymous(self):
408 # anonymous mmap.mmap(-1, PAGE)
409 m = mmap.mmap(-1, PAGESIZE)
Guido van Rossum805365e2007-05-07 22:24:25 +0000410 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000411 self.assertEqual(m[x], 0,
412 "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000413
Guido van Rossum805365e2007-05-07 22:24:25 +0000414 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000415 b = x & 0xff
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000416 m[x] = b
417 self.assertEqual(m[x], b)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000418
Thomas Woutersed03b412007-08-28 21:37:11 +0000419 def test_extended_getslice(self):
420 # Test extended slicing by comparing with list slicing.
421 s = bytes(reversed(range(256)))
422 m = mmap.mmap(-1, len(s))
423 m[:] = s
424 self.assertEqual(m[:], s)
425 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
426 for start in indices:
427 for stop in indices:
428 # Skip step 0 (invalid)
429 for step in indices[1:]:
430 self.assertEqual(m[start:stop:step],
431 s[start:stop:step])
432
433 def test_extended_set_del_slice(self):
434 # Test extended slicing by comparing with list slicing.
435 s = bytes(reversed(range(256)))
436 m = mmap.mmap(-1, len(s))
437 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
438 for start in indices:
439 for stop in indices:
440 # Skip invalid step 0
441 for step in indices[1:]:
442 m[:] = s
443 self.assertEqual(m[:], s)
444 L = list(s)
445 # Make sure we have a slice of exactly the right length,
446 # but with different data.
447 data = L[start:stop:step]
448 data = bytes(reversed(data))
449 L[start:stop:step] = data
450 m[start:stop:step] = data
Ezio Melottib3aedd42010-11-20 19:04:17 +0000451 self.assertEqual(m[:], bytes(L))
Thomas Woutersed03b412007-08-28 21:37:11 +0000452
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000453 def make_mmap_file (self, f, halfsize):
454 # Write 2 pages worth of data to the file
455 f.write (b'\0' * halfsize)
456 f.write (b'foo')
457 f.write (b'\0' * (halfsize - 3))
458 f.flush ()
459 return mmap.mmap (f.fileno(), 0)
460
461 def test_offset (self):
462 f = open (TESTFN, 'w+b')
463
464 try: # unlink TESTFN no matter what
465 halfsize = mmap.ALLOCATIONGRANULARITY
466 m = self.make_mmap_file (f, halfsize)
467 m.close ()
468 f.close ()
469
470 mapsize = halfsize * 2
471 # Try invalid offset
472 f = open(TESTFN, "r+b")
473 for offset in [-2, -1, None]:
474 try:
475 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
476 self.assertEqual(0, 1)
477 except (ValueError, TypeError, OverflowError):
478 pass
479 else:
480 self.assertEqual(0, 0)
481 f.close()
482
483 # Try valid offset, hopefully 8192 works on all OSes
484 f = open(TESTFN, "r+b")
485 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
486 self.assertEqual(m[0:3], b'foo')
487 f.close()
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000488
489 # Try resizing map
490 try:
491 m.resize(512)
492 except SystemError:
493 pass
494 else:
495 # resize() is supported
496 self.assertEqual(len(m), 512)
497 # Check that we can no longer seek beyond the new size.
498 self.assertRaises(ValueError, m.seek, 513, 0)
499 # Check that the content is not changed
500 self.assertEqual(m[0:3], b'foo')
501
502 # Check that the underlying file is truncated too
503 f = open(TESTFN)
504 f.seek(0, 2)
505 self.assertEqual(f.tell(), halfsize + 512)
506 f.close()
507 self.assertEqual(m.size(), halfsize + 512)
508
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000509 m.close()
510
511 finally:
512 f.close()
513 try:
514 os.unlink(TESTFN)
515 except OSError:
516 pass
517
Christian Heimes1af737c2008-01-23 08:24:23 +0000518 def test_subclass(self):
519 class anon_mmap(mmap.mmap):
520 def __new__(klass, *args, **kwargs):
521 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
522 anon_mmap(PAGESIZE)
523
Christian Heimesa156e092008-02-16 07:38:31 +0000524 def test_prot_readonly(self):
Christian Heimes18c66892008-02-17 13:31:39 +0000525 if not hasattr(mmap, 'PROT_READ'):
526 return
Christian Heimesa156e092008-02-16 07:38:31 +0000527 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000528 with open(TESTFN, "wb") as fp:
529 fp.write(b"a"*mapsize)
Christian Heimesa156e092008-02-16 07:38:31 +0000530 f = open(TESTFN, "rb")
531 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
532 self.assertRaises(TypeError, m.write, "foo")
Martin v. Löwise1e9f232008-04-01 06:17:46 +0000533 f.close()
Christian Heimes1af737c2008-01-23 08:24:23 +0000534
Christian Heimes7131fd92008-02-19 14:21:46 +0000535 def test_error(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000536 self.assertTrue(issubclass(mmap.error, EnvironmentError))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000537 self.assertIn("mmap.error", str(mmap.error))
Christian Heimes7131fd92008-02-19 14:21:46 +0000538
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000539 def test_io_methods(self):
540 data = b"0123456789"
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000541 with open(TESTFN, "wb") as fp:
542 fp.write(b"x"*len(data))
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000543 f = open(TESTFN, "r+b")
544 m = mmap.mmap(f.fileno(), len(data))
545 f.close()
546 # Test write_byte()
547 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000548 self.assertEqual(m.tell(), i)
Benjamin Petersone099b372009-04-04 17:09:35 +0000549 m.write_byte(data[i])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000550 self.assertEqual(m.tell(), i+1)
Benjamin Petersone099b372009-04-04 17:09:35 +0000551 self.assertRaises(ValueError, m.write_byte, b"x"[0])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000552 self.assertEqual(m[:], data)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000553 # Test read_byte()
554 m.seek(0)
555 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000556 self.assertEqual(m.tell(), i)
557 self.assertEqual(m.read_byte(), data[i])
558 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000559 self.assertRaises(ValueError, m.read_byte)
560 # Test read()
561 m.seek(3)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000562 self.assertEqual(m.read(3), b"345")
563 self.assertEqual(m.tell(), 6)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000564 # Test write()
565 m.seek(3)
566 m.write(b"bar")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000567 self.assertEqual(m.tell(), 6)
568 self.assertEqual(m[:], b"012bar6789")
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000569 m.seek(8)
570 self.assertRaises(ValueError, m.write, b"bar")
571
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000572 def test_non_ascii_byte(self):
573 for b in (129, 200, 255): # > 128
574 m = mmap.mmap(-1, 1)
575 m.write_byte(b)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000576 self.assertEqual(m[0], b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000577 m.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000578 self.assertEqual(m.read_byte(), b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000579 m.close()
580
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000581 if os.name == 'nt':
582 def test_tagname(self):
583 data1 = b"0123456789"
584 data2 = b"abcdefghij"
585 assert len(data1) == len(data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000586
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000587 # Test same tag
588 m1 = mmap.mmap(-1, len(data1), tagname="foo")
589 m1[:] = data1
590 m2 = mmap.mmap(-1, len(data2), tagname="foo")
591 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000592 self.assertEqual(m1[:], data2)
593 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000594 m2.close()
595 m1.close()
596
Ezio Melotti13925002011-03-16 11:05:33 +0200597 # Test different tag
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000598 m1 = mmap.mmap(-1, len(data1), tagname="foo")
599 m1[:] = data1
600 m2 = mmap.mmap(-1, len(data2), tagname="boo")
601 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000602 self.assertEqual(m1[:], data1)
603 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000604 m2.close()
605 m1.close()
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000606
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000607 def test_crasher_on_windows(self):
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000608 # Should not crash (Issue 1733986)
609 m = mmap.mmap(-1, 1000, tagname="foo")
610 try:
611 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
612 except:
613 pass
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000614 m.close()
615
616 # Should not crash (Issue 5385)
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000617 with open(TESTFN, "wb") as fp:
618 fp.write(b"x"*10)
Hirokazu Yamamoto9b789252009-03-05 15:00:28 +0000619 f = open(TESTFN, "r+b")
620 m = mmap.mmap(f.fileno(), 0)
621 f.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000622 try:
Hirokazu Yamamoto9b789252009-03-05 15:00:28 +0000623 m.resize(0) # will raise WindowsError
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000624 except:
625 pass
626 try:
627 m[:]
628 except:
629 pass
630 m.close()
631
Brian Curtinea47eaa2010-08-01 15:26:26 +0000632 def test_invalid_descriptor(self):
633 # socket file descriptors are valid, but out of range
634 # for _get_osfhandle, causing a crash when validating the
635 # parameters to _get_osfhandle.
636 s = socket.socket()
637 try:
638 with self.assertRaises(mmap.error):
639 m = mmap.mmap(s.fileno(), 10)
640 finally:
641 s.close()
642
Georg Brandl0bccc182010-08-01 14:50:00 +0000643 def test_context_manager(self):
644 with mmap.mmap(-1, 10) as m:
645 self.assertFalse(m.closed)
646 self.assertTrue(m.closed)
647
648 def test_context_manager_exception(self):
649 # Test that the IOError gets passed through
650 with self.assertRaises(Exception) as exc:
651 with mmap.mmap(-1, 10) as m:
652 raise IOError
653 self.assertIsInstance(exc.exception, IOError,
654 "wrong exception raised in context manager")
655 self.assertTrue(m.closed, "context manager failed")
656
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000657class LargeMmapTests(unittest.TestCase):
658
659 def setUp(self):
660 unlink(TESTFN)
661
662 def tearDown(self):
663 unlink(TESTFN)
664
665 def _working_largefile(self):
666 # Only run if the current filesystem supports large files.
667 f = open(TESTFN, 'wb', buffering=0)
668 try:
669 f.seek(0x80000001)
670 f.write(b'x')
671 f.flush()
672 except (IOError, OverflowError):
673 raise unittest.SkipTest("filesystem does not have largefile support")
674 finally:
675 f.close()
676 unlink(TESTFN)
677
678 def test_large_offset(self):
679 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
680 requires('largefile',
681 'test requires %s bytes and a long time to run' % str(0x180000000))
682 self._working_largefile()
683 with open(TESTFN, 'wb') as f:
684 f.seek(0x14FFFFFFF)
685 f.write(b" ")
686
687 with open(TESTFN, 'rb') as f:
688 with mmap.mmap(f.fileno(), 0, offset=0x140000000, access=mmap.ACCESS_READ) as m:
689 self.assertEqual(m[0xFFFFFFF], 32)
690
691 def test_large_filesize(self):
692 if sys.platform[:3] == 'win' or sys.platform == 'darwin':
693 requires('largefile',
694 'test requires %s bytes and a long time to run' % str(0x180000000))
695 self._working_largefile()
696 with open(TESTFN, 'wb') as f:
697 f.seek(0x17FFFFFFF)
698 f.write(b" ")
699
700 with open(TESTFN, 'rb') as f:
701 with mmap.mmap(f.fileno(), 0x10000, access=mmap.ACCESS_READ) as m:
702 self.assertEqual(m.size(), 0x180000000)
703
Christian Heimes7131fd92008-02-19 14:21:46 +0000704
Thomas Wouters89f507f2006-12-13 04:49:30 +0000705def test_main():
Antoine Pitrou97696cb2011-02-21 23:46:27 +0000706 run_unittest(MmapTests, LargeMmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000707
Thomas Wouters89f507f2006-12-13 04:49:30 +0000708if __name__ == '__main__':
709 test_main()