blob: c7b8d60d1e53aa63207df8d64470d3a884999a9d [file] [log] [blame]
R. David Murraya21e4ca2009-03-31 23:16:50 +00001from test.support import TESTFN, run_unittest, import_module
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
Thomas Wouters89f507f2006-12-13 04:49:30 +0000237 def test_bad_file_desc(self):
238 # Try opening a bad file descriptor...
239 self.assertRaises(mmap.error, mmap.mmap, -2, 4096)
Neal Norwitz3b4fff82006-01-11 08:54:45 +0000240
Thomas Wouters89f507f2006-12-13 04:49:30 +0000241 def test_tougher_find(self):
242 # Do a tougher .find() test. SF bug 515943 pointed out that, in 2.2,
243 # searching for data with embedded \0 bytes didn't work.
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000244 with open(TESTFN, 'wb+') as f:
Tim Petersc9ffa062002-03-08 05:43:32 +0000245
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000246 data = b'aabaac\x00deef\x00\x00aa\x00'
247 n = len(data)
248 f.write(data)
249 f.flush()
250 m = mmap.mmap(f.fileno(), n)
Tim Petersc9ffa062002-03-08 05:43:32 +0000251
252 for start in range(n+1):
253 for finish in range(start, n+1):
254 slice = data[start : finish]
Thomas Wouters89f507f2006-12-13 04:49:30 +0000255 self.assertEqual(m.find(slice), data.find(slice))
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000256 self.assertEqual(m.find(slice + b'x'), -1)
Tim Petersddc82ea2003-01-13 21:38:45 +0000257 m.close()
Tim Petersc9ffa062002-03-08 05:43:32 +0000258
Georg Brandlfceab5a2008-01-19 20:08:23 +0000259 def test_find_end(self):
260 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000261 f = open(TESTFN, 'wb+')
262 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000263 n = len(data)
264 f.write(data)
265 f.flush()
266 m = mmap.mmap(f.fileno(), n)
267 f.close()
268
Benjamin Petersone099b372009-04-04 17:09:35 +0000269 self.assertEqual(m.find(b'one'), 0)
270 self.assertEqual(m.find(b'ones'), 8)
271 self.assertEqual(m.find(b'one', 0, -1), 0)
272 self.assertEqual(m.find(b'one', 1), 8)
273 self.assertEqual(m.find(b'one', 1, -1), 8)
274 self.assertEqual(m.find(b'one', 1, -2), -1)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000275
276
277 def test_rfind(self):
278 # test the new 'end' parameter works as expected
Benjamin Petersone099b372009-04-04 17:09:35 +0000279 f = open(TESTFN, 'wb+')
280 data = b'one two ones'
Georg Brandlfceab5a2008-01-19 20:08:23 +0000281 n = len(data)
282 f.write(data)
283 f.flush()
284 m = mmap.mmap(f.fileno(), n)
285 f.close()
286
Benjamin Petersone099b372009-04-04 17:09:35 +0000287 self.assertEqual(m.rfind(b'one'), 8)
288 self.assertEqual(m.rfind(b'one '), 0)
289 self.assertEqual(m.rfind(b'one', 0, -1), 8)
290 self.assertEqual(m.rfind(b'one', 0, -2), 0)
291 self.assertEqual(m.rfind(b'one', 1, -1), 8)
292 self.assertEqual(m.rfind(b'one', 1, -2), -1)
Georg Brandlfceab5a2008-01-19 20:08:23 +0000293
294
Thomas Wouters89f507f2006-12-13 04:49:30 +0000295 def test_double_close(self):
296 # make sure a double close doesn't crash on Solaris (Bug# 665913)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000297 f = open(TESTFN, 'wb+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000298
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000299 f.write(2**16 * b'a') # Arbitrary character
Neal Norwitze604c022003-01-10 20:52:16 +0000300 f.close()
301
302 f = open(TESTFN)
Tim Petersddc82ea2003-01-13 21:38:45 +0000303 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
Neal Norwitze604c022003-01-10 20:52:16 +0000304 mf.close()
305 mf.close()
306 f.close()
307
Thomas Wouters89f507f2006-12-13 04:49:30 +0000308 def test_entire_file(self):
309 # test mapping of entire file by passing 0 for map length
310 if hasattr(os, "stat"):
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000311 f = open(TESTFN, "wb+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000312
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000313 f.write(2**16 * b'm') # Arbitrary character
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000314 f.close()
315
316 f = open(TESTFN, "rb+")
Tim Peterseba28be2005-03-28 01:08:02 +0000317 mf = mmap.mmap(f.fileno(), 0)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000318 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000319 self.assertEqual(mf.read(2**16), 2**16 * b"m")
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000320 mf.close()
321 f.close()
322
Antoine Pitrou85f46152011-01-15 16:17:07 +0000323 def test_length_0_offset(self):
324 # Issue #10916: test mapping of remainder of file by passing 0 for
325 # map length with an offset doesn't cause a segfault.
326 if not hasattr(os, "stat"):
327 self.skipTest("needs os.stat")
328 with open(TESTFN, "wb+") as f:
329 f.write(49152 * b'm') # Arbitrary character
330
331 with open(TESTFN, "rb") as f:
332 mf = mmap.mmap(f.fileno(), 0, offset=40960, access=mmap.ACCESS_READ)
333 self.assertRaises(IndexError, mf.__getitem__, 45000)
334 mf.close()
335
Thomas Wouters89f507f2006-12-13 04:49:30 +0000336 def test_move(self):
337 # make move works everywhere (64-bit format problem earlier)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000338 f = open(TESTFN, 'wb+')
Tim Peterseba28be2005-03-28 01:08:02 +0000339
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000340 f.write(b"ABCDEabcde") # Arbitrary character
Neal Norwitz8856fb72005-12-18 03:34:22 +0000341 f.flush()
342
343 mf = mmap.mmap(f.fileno(), 10)
344 mf.move(5, 0, 5)
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000345 self.assertEqual(mf[:], b"ABCDEABCDE", "Map move should have duplicated front 5")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000346 mf.close()
347 f.close()
348
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000349 # more excessive test
350 data = b"0123456789"
351 for dest in range(len(data)):
352 for src in range(len(data)):
353 for count in range(len(data) - max(dest, src)):
354 expected = data[:dest] + data[src:src+count] + data[dest+count:]
355 m = mmap.mmap(-1, len(data))
356 m[:] = data
357 m.move(dest, src, count)
358 self.assertEqual(m[:], expected)
359 m.close()
360
Hirokazu Yamamoto2ca15012009-03-31 20:43:56 +0000361 # segfault test (Issue 5387)
362 m = mmap.mmap(-1, 100)
363 offsets = [-100, -1, 0, 1, 100]
364 for source, dest, size in itertools.product(offsets, offsets, offsets):
365 try:
366 m.move(source, dest, size)
367 except ValueError:
368 pass
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000369
370 offsets = [(-1, -1, -1), (-1, -1, 0), (-1, 0, -1), (0, -1, -1),
371 (-1, 0, 0), (0, -1, 0), (0, 0, -1)]
372 for source, dest, size in offsets:
373 self.assertRaises(ValueError, m.move, source, dest, size)
374
Hirokazu Yamamoto16caab02009-03-31 13:44:06 +0000375 m.close()
376
Benjamin Petersonef3e4c22009-04-11 19:48:14 +0000377 m = mmap.mmap(-1, 1) # single byte
378 self.assertRaises(ValueError, m.move, 0, 0, 2)
379 self.assertRaises(ValueError, m.move, 1, 0, 1)
380 self.assertRaises(ValueError, m.move, 0, 1, 1)
381 m.move(0, 0, 1)
382 m.move(0, 0, 0)
383
384
Thomas Wouters89f507f2006-12-13 04:49:30 +0000385 def test_anonymous(self):
386 # anonymous mmap.mmap(-1, PAGE)
387 m = mmap.mmap(-1, PAGESIZE)
Guido van Rossum805365e2007-05-07 22:24:25 +0000388 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000389 self.assertEqual(m[x], 0,
390 "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000391
Guido van Rossum805365e2007-05-07 22:24:25 +0000392 for x in range(PAGESIZE):
Guido van Rossum98297ee2007-11-06 21:34:58 +0000393 b = x & 0xff
Guido van Rossumb358a2c2007-07-16 19:29:02 +0000394 m[x] = b
395 self.assertEqual(m[x], b)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000396
Thomas Woutersed03b412007-08-28 21:37:11 +0000397 def test_extended_getslice(self):
398 # Test extended slicing by comparing with list slicing.
399 s = bytes(reversed(range(256)))
400 m = mmap.mmap(-1, len(s))
401 m[:] = s
402 self.assertEqual(m[:], s)
403 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
404 for start in indices:
405 for stop in indices:
406 # Skip step 0 (invalid)
407 for step in indices[1:]:
408 self.assertEqual(m[start:stop:step],
409 s[start:stop:step])
410
411 def test_extended_set_del_slice(self):
412 # Test extended slicing by comparing with list slicing.
413 s = bytes(reversed(range(256)))
414 m = mmap.mmap(-1, len(s))
415 indices = (0, None, 1, 3, 19, 300, -1, -2, -31, -300)
416 for start in indices:
417 for stop in indices:
418 # Skip invalid step 0
419 for step in indices[1:]:
420 m[:] = s
421 self.assertEqual(m[:], s)
422 L = list(s)
423 # Make sure we have a slice of exactly the right length,
424 # but with different data.
425 data = L[start:stop:step]
426 data = bytes(reversed(data))
427 L[start:stop:step] = data
428 m[start:stop:step] = data
Ezio Melottib3aedd42010-11-20 19:04:17 +0000429 self.assertEqual(m[:], bytes(L))
Thomas Woutersed03b412007-08-28 21:37:11 +0000430
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000431 def make_mmap_file (self, f, halfsize):
432 # Write 2 pages worth of data to the file
433 f.write (b'\0' * halfsize)
434 f.write (b'foo')
435 f.write (b'\0' * (halfsize - 3))
436 f.flush ()
437 return mmap.mmap (f.fileno(), 0)
438
439 def test_offset (self):
440 f = open (TESTFN, 'w+b')
441
442 try: # unlink TESTFN no matter what
443 halfsize = mmap.ALLOCATIONGRANULARITY
444 m = self.make_mmap_file (f, halfsize)
445 m.close ()
446 f.close ()
447
448 mapsize = halfsize * 2
449 # Try invalid offset
450 f = open(TESTFN, "r+b")
451 for offset in [-2, -1, None]:
452 try:
453 m = mmap.mmap(f.fileno(), mapsize, offset=offset)
454 self.assertEqual(0, 1)
455 except (ValueError, TypeError, OverflowError):
456 pass
457 else:
458 self.assertEqual(0, 0)
459 f.close()
460
461 # Try valid offset, hopefully 8192 works on all OSes
462 f = open(TESTFN, "r+b")
463 m = mmap.mmap(f.fileno(), mapsize - halfsize, offset=halfsize)
464 self.assertEqual(m[0:3], b'foo')
465 f.close()
Hirokazu Yamamoto0654ccd2009-02-18 16:38:00 +0000466
467 # Try resizing map
468 try:
469 m.resize(512)
470 except SystemError:
471 pass
472 else:
473 # resize() is supported
474 self.assertEqual(len(m), 512)
475 # Check that we can no longer seek beyond the new size.
476 self.assertRaises(ValueError, m.seek, 513, 0)
477 # Check that the content is not changed
478 self.assertEqual(m[0:3], b'foo')
479
480 # Check that the underlying file is truncated too
481 f = open(TESTFN)
482 f.seek(0, 2)
483 self.assertEqual(f.tell(), halfsize + 512)
484 f.close()
485 self.assertEqual(m.size(), halfsize + 512)
486
Guido van Rossum8ce8a782007-11-01 19:42:39 +0000487 m.close()
488
489 finally:
490 f.close()
491 try:
492 os.unlink(TESTFN)
493 except OSError:
494 pass
495
Christian Heimes1af737c2008-01-23 08:24:23 +0000496 def test_subclass(self):
497 class anon_mmap(mmap.mmap):
498 def __new__(klass, *args, **kwargs):
499 return mmap.mmap.__new__(klass, -1, *args, **kwargs)
500 anon_mmap(PAGESIZE)
501
Christian Heimesa156e092008-02-16 07:38:31 +0000502 def test_prot_readonly(self):
Christian Heimes18c66892008-02-17 13:31:39 +0000503 if not hasattr(mmap, 'PROT_READ'):
504 return
Christian Heimesa156e092008-02-16 07:38:31 +0000505 mapsize = 10
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000506 with open(TESTFN, "wb") as fp:
507 fp.write(b"a"*mapsize)
Christian Heimesa156e092008-02-16 07:38:31 +0000508 f = open(TESTFN, "rb")
509 m = mmap.mmap(f.fileno(), mapsize, prot=mmap.PROT_READ)
510 self.assertRaises(TypeError, m.write, "foo")
Martin v. Löwise1e9f232008-04-01 06:17:46 +0000511 f.close()
Christian Heimes1af737c2008-01-23 08:24:23 +0000512
Christian Heimes7131fd92008-02-19 14:21:46 +0000513 def test_error(self):
Benjamin Petersonc9c0f202009-06-30 23:06:06 +0000514 self.assertTrue(issubclass(mmap.error, EnvironmentError))
Benjamin Peterson577473f2010-01-19 00:09:57 +0000515 self.assertIn("mmap.error", str(mmap.error))
Christian Heimes7131fd92008-02-19 14:21:46 +0000516
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000517 def test_io_methods(self):
518 data = b"0123456789"
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000519 with open(TESTFN, "wb") as fp:
520 fp.write(b"x"*len(data))
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000521 f = open(TESTFN, "r+b")
522 m = mmap.mmap(f.fileno(), len(data))
523 f.close()
524 # Test write_byte()
525 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000526 self.assertEqual(m.tell(), i)
Benjamin Petersone099b372009-04-04 17:09:35 +0000527 m.write_byte(data[i])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000528 self.assertEqual(m.tell(), i+1)
Benjamin Petersone099b372009-04-04 17:09:35 +0000529 self.assertRaises(ValueError, m.write_byte, b"x"[0])
Ezio Melottib3aedd42010-11-20 19:04:17 +0000530 self.assertEqual(m[:], data)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000531 # Test read_byte()
532 m.seek(0)
533 for i in range(len(data)):
Ezio Melottib3aedd42010-11-20 19:04:17 +0000534 self.assertEqual(m.tell(), i)
535 self.assertEqual(m.read_byte(), data[i])
536 self.assertEqual(m.tell(), i+1)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000537 self.assertRaises(ValueError, m.read_byte)
538 # Test read()
539 m.seek(3)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000540 self.assertEqual(m.read(3), b"345")
541 self.assertEqual(m.tell(), 6)
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000542 # Test write()
543 m.seek(3)
544 m.write(b"bar")
Ezio Melottib3aedd42010-11-20 19:04:17 +0000545 self.assertEqual(m.tell(), 6)
546 self.assertEqual(m[:], b"012bar6789")
Hirokazu Yamamoto39c6dea2009-02-28 10:56:50 +0000547 m.seek(8)
548 self.assertRaises(ValueError, m.write, b"bar")
549
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000550 def test_non_ascii_byte(self):
551 for b in (129, 200, 255): # > 128
552 m = mmap.mmap(-1, 1)
553 m.write_byte(b)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000554 self.assertEqual(m[0], b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000555 m.seek(0)
Ezio Melottib3aedd42010-11-20 19:04:17 +0000556 self.assertEqual(m.read_byte(), b)
Hirokazu Yamamoto3cdd5cb2010-11-04 12:09:08 +0000557 m.close()
558
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000559 if os.name == 'nt':
560 def test_tagname(self):
561 data1 = b"0123456789"
562 data2 = b"abcdefghij"
563 assert len(data1) == len(data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000564
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000565 # Test same tag
566 m1 = mmap.mmap(-1, len(data1), tagname="foo")
567 m1[:] = data1
568 m2 = mmap.mmap(-1, len(data2), tagname="foo")
569 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000570 self.assertEqual(m1[:], data2)
571 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000572 m2.close()
573 m1.close()
574
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000575 # Test differnt tag
576 m1 = mmap.mmap(-1, len(data1), tagname="foo")
577 m1[:] = data1
578 m2 = mmap.mmap(-1, len(data2), tagname="boo")
579 m2[:] = data2
Ezio Melottib3aedd42010-11-20 19:04:17 +0000580 self.assertEqual(m1[:], data1)
581 self.assertEqual(m2[:], data2)
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000582 m2.close()
583 m1.close()
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000584
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000585 def test_crasher_on_windows(self):
Hirokazu Yamamoto10c99372009-02-28 12:21:53 +0000586 # Should not crash (Issue 1733986)
587 m = mmap.mmap(-1, 1000, tagname="foo")
588 try:
589 mmap.mmap(-1, 5000, tagname="foo")[:] # same tagname, but larger size
590 except:
591 pass
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000592 m.close()
593
594 # Should not crash (Issue 5385)
Benjamin Peterson5dc8fab2010-10-31 01:44:49 +0000595 with open(TESTFN, "wb") as fp:
596 fp.write(b"x"*10)
Hirokazu Yamamoto9b789252009-03-05 15:00:28 +0000597 f = open(TESTFN, "r+b")
598 m = mmap.mmap(f.fileno(), 0)
599 f.close()
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000600 try:
Hirokazu Yamamoto9b789252009-03-05 15:00:28 +0000601 m.resize(0) # will raise WindowsError
Hirokazu Yamamoto8e722bc2009-03-05 14:33:01 +0000602 except:
603 pass
604 try:
605 m[:]
606 except:
607 pass
608 m.close()
609
Brian Curtinea47eaa2010-08-01 15:26:26 +0000610 def test_invalid_descriptor(self):
611 # socket file descriptors are valid, but out of range
612 # for _get_osfhandle, causing a crash when validating the
613 # parameters to _get_osfhandle.
614 s = socket.socket()
615 try:
616 with self.assertRaises(mmap.error):
617 m = mmap.mmap(s.fileno(), 10)
618 finally:
619 s.close()
620
Georg Brandl0bccc182010-08-01 14:50:00 +0000621 def test_context_manager(self):
622 with mmap.mmap(-1, 10) as m:
623 self.assertFalse(m.closed)
624 self.assertTrue(m.closed)
625
626 def test_context_manager_exception(self):
627 # Test that the IOError gets passed through
628 with self.assertRaises(Exception) as exc:
629 with mmap.mmap(-1, 10) as m:
630 raise IOError
631 self.assertIsInstance(exc.exception, IOError,
632 "wrong exception raised in context manager")
633 self.assertTrue(m.closed, "context manager failed")
634
Christian Heimes7131fd92008-02-19 14:21:46 +0000635
Thomas Wouters89f507f2006-12-13 04:49:30 +0000636def test_main():
637 run_unittest(MmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000638
Thomas Wouters89f507f2006-12-13 04:49:30 +0000639if __name__ == '__main__':
640 test_main()