blob: d2a24770cf203d74620ae71ca14c4e794a9e1064 [file] [log] [blame]
Barry Warsaw04f357c2002-07-23 19:04:11 +00001from test.test_support import verify, vereq, TESTFN
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +00002import mmap
Fred Drake62787992001-05-11 14:29:21 +00003import os, re
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +00004
5PAGESIZE = mmap.PAGESIZE
6
Guido van Rossum767e7752000-03-31 01:09:14 +00007def test_both():
8 "Test mmap module on Unix systems and Windows"
Fred Drake004d5e62000-10-23 17:22:08 +00009
Tim Petersfd692082001-05-10 20:03:04 +000010 # Create a file to be mmap'ed.
Fred Drake62787992001-05-11 14:29:21 +000011 if os.path.exists(TESTFN):
12 os.unlink(TESTFN)
Tim Petersfd692082001-05-10 20:03:04 +000013 f = open(TESTFN, 'w+')
Fred Drake004d5e62000-10-23 17:22:08 +000014
Tim Petersfd692082001-05-10 20:03:04 +000015 try: # unlink TESTFN no matter what
16 # Write 2 pages worth of data to the file
17 f.write('\0'* PAGESIZE)
18 f.write('foo')
19 f.write('\0'* (PAGESIZE-3) )
Tim Peterse12cda92002-04-23 23:07:28 +000020 f.flush()
Tim Petersfd692082001-05-10 20:03:04 +000021 m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
22 f.close()
Fred Drake004d5e62000-10-23 17:22:08 +000023
Tim Petersfd692082001-05-10 20:03:04 +000024 # Simple sanity checks
Tim Peters2caf8df2001-01-14 05:05:51 +000025
Tim Petersfd692082001-05-10 20:03:04 +000026 print type(m) # SF bug 128713: segfaulted on Linux
27 print ' Position of foo:', m.find('foo') / float(PAGESIZE), 'pages'
Tim Peters5ebfd362001-11-13 23:11:19 +000028 vereq(m.find('foo'), PAGESIZE)
Fred Drake004d5e62000-10-23 17:22:08 +000029
Tim Petersfd692082001-05-10 20:03:04 +000030 print ' Length of file:', len(m) / float(PAGESIZE), 'pages'
Tim Peters5ebfd362001-11-13 23:11:19 +000031 vereq(len(m), 2*PAGESIZE)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +000032
Tim Petersfd692082001-05-10 20:03:04 +000033 print ' Contents of byte 0:', repr(m[0])
Tim Peters5ebfd362001-11-13 23:11:19 +000034 vereq(m[0], '\0')
Tim Petersfd692082001-05-10 20:03:04 +000035 print ' Contents of first 3 bytes:', repr(m[0:3])
Tim Peters5ebfd362001-11-13 23:11:19 +000036 vereq(m[0:3], '\0\0\0')
Fred Drake004d5e62000-10-23 17:22:08 +000037
Tim Petersfd692082001-05-10 20:03:04 +000038 # Modify the file's content
39 print "\n Modifying file's content..."
40 m[0] = '3'
Fred Drake62787992001-05-11 14:29:21 +000041 m[PAGESIZE +3: PAGESIZE +3+3] = 'bar'
Fred Drake004d5e62000-10-23 17:22:08 +000042
Tim Petersfd692082001-05-10 20:03:04 +000043 # Check that the modification worked
44 print ' Contents of byte 0:', repr(m[0])
Tim Peters5ebfd362001-11-13 23:11:19 +000045 vereq(m[0], '3')
Tim Petersfd692082001-05-10 20:03:04 +000046 print ' Contents of first 3 bytes:', repr(m[0:3])
Tim Peters5ebfd362001-11-13 23:11:19 +000047 vereq(m[0:3], '3\0\0')
Tim Petersfd692082001-05-10 20:03:04 +000048 print ' Contents of second page:', repr(m[PAGESIZE-1 : PAGESIZE + 7])
Tim Peters5ebfd362001-11-13 23:11:19 +000049 vereq(m[PAGESIZE-1 : PAGESIZE + 7], '\0foobar\0')
Fred Drake004d5e62000-10-23 17:22:08 +000050
Tim Petersfd692082001-05-10 20:03:04 +000051 m.flush()
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +000052
Tim Petersfd692082001-05-10 20:03:04 +000053 # Test doing a regular expression match in an mmap'ed file
Fred Drake62787992001-05-11 14:29:21 +000054 match = re.search('[A-Za-z]+', m)
Tim Petersfd692082001-05-10 20:03:04 +000055 if match is None:
56 print ' ERROR: regex match on mmap failed!'
57 else:
58 start, end = match.span(0)
59 length = end - start
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +000060
Tim Petersfd692082001-05-10 20:03:04 +000061 print ' Regex match on mmap (page start, length of match):',
62 print start / float(PAGESIZE), length
Fred Drake004d5e62000-10-23 17:22:08 +000063
Tim Peters5ebfd362001-11-13 23:11:19 +000064 vereq(start, PAGESIZE)
65 vereq(end, PAGESIZE + 6)
Andrew M. Kuchlingcf70ea62000-06-18 04:47:08 +000066
Tim Petersfd692082001-05-10 20:03:04 +000067 # test seeking around (try to overflow the seek implementation)
68 m.seek(0,0)
69 print ' Seek to zeroth byte'
Tim Peters5ebfd362001-11-13 23:11:19 +000070 vereq(m.tell(), 0)
Tim Petersfd692082001-05-10 20:03:04 +000071 m.seek(42,1)
72 print ' Seek to 42nd byte'
Tim Peters5ebfd362001-11-13 23:11:19 +000073 vereq(m.tell(), 42)
Tim Petersfd692082001-05-10 20:03:04 +000074 m.seek(0,2)
75 print ' Seek to last byte'
Tim Peters5ebfd362001-11-13 23:11:19 +000076 vereq(m.tell(), len(m))
Peter Schneider-Kamp7a116712000-07-11 11:24:41 +000077
Tim Petersfd692082001-05-10 20:03:04 +000078 print ' Try to seek to negative position...'
Thomas Woutersa7015972000-07-30 15:38:35 +000079 try:
Tim Petersfd692082001-05-10 20:03:04 +000080 m.seek(-1)
Thomas Woutersa7015972000-07-30 15:38:35 +000081 except ValueError:
82 pass
83 else:
Tim Petersfd692082001-05-10 20:03:04 +000084 verify(0, 'expected a ValueError but did not get it')
Fred Drake004d5e62000-10-23 17:22:08 +000085
Tim Petersfd692082001-05-10 20:03:04 +000086 print ' Try to seek beyond end of mmap...'
87 try:
88 m.seek(1,2)
89 except ValueError:
90 pass
91 else:
92 verify(0, 'expected a ValueError but did not get it')
93
94 print ' Try to seek to negative position...'
95 try:
96 m.seek(-len(m)-1,2)
97 except ValueError:
98 pass
99 else:
100 verify(0, 'expected a ValueError but did not get it')
101
102 # Try resizing map
103 print ' Attempting resize()'
104 try:
Tim Peters4f4f4d72002-09-10 20:49:15 +0000105 m.resize(512)
Tim Petersfd692082001-05-10 20:03:04 +0000106 except SystemError:
107 # resize() not supported
108 # No messages are printed, since the output of this test suite
109 # would then be different across platforms.
110 pass
111 else:
112 # resize() is supported
113 verify(len(m) == 512,
114 "len(m) is %d, but expecting 512" % (len(m),) )
115 # Check that we can no longer seek beyond the new size.
116 try:
117 m.seek(513,0)
118 except ValueError:
119 pass
120 else:
121 verify(0, 'Could seek beyond the new size')
122
Georg Brandl38387b82005-08-24 07:17:40 +0000123 # Check that the underlying file is truncated too
124 # (bug #728515)
125 f = open(TESTFN)
126 f.seek(0, 2)
127 verify(f.tell() == 512, 'Underlying file not truncated')
128 f.close()
Tim Peters9e34c042005-08-26 15:20:46 +0000129 verify(m.size() == 512, 'New size not reflected in file')
Georg Brandl38387b82005-08-24 07:17:40 +0000130
Tim Petersfd692082001-05-10 20:03:04 +0000131 m.close()
132
133 finally:
134 try:
135 f.close()
136 except OSError:
137 pass
138 try:
Fred Drake62787992001-05-11 14:29:21 +0000139 os.unlink(TESTFN)
Tim Petersfd692082001-05-10 20:03:04 +0000140 except OSError:
141 pass
142
Tim Peters5ebfd362001-11-13 23:11:19 +0000143 # Test for "access" keyword parameter
144 try:
145 mapsize = 10
146 print " Creating", mapsize, "byte test data file."
147 open(TESTFN, "wb").write("a"*mapsize)
148 print " Opening mmap with access=ACCESS_READ"
149 f = open(TESTFN, "rb")
150 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_READ)
151 verify(m[:] == 'a'*mapsize, "Readonly memory map data incorrect.")
152
153 print " Ensuring that readonly mmap can't be slice assigned."
154 try:
155 m[:] = 'b'*mapsize
156 except TypeError:
157 pass
158 else:
159 verify(0, "Able to write to readonly memory map")
160
161 print " Ensuring that readonly mmap can't be item assigned."
162 try:
163 m[0] = 'b'
164 except TypeError:
165 pass
166 else:
167 verify(0, "Able to write to readonly memory map")
168
169 print " Ensuring that readonly mmap can't be write() to."
170 try:
171 m.seek(0,0)
172 m.write('abc')
173 except TypeError:
174 pass
175 else:
176 verify(0, "Able to write to readonly memory map")
177
178 print " Ensuring that readonly mmap can't be write_byte() to."
179 try:
180 m.seek(0,0)
181 m.write_byte('d')
182 except TypeError:
183 pass
184 else:
185 verify(0, "Able to write to readonly memory map")
186
187 print " Ensuring that readonly mmap can't be resized."
188 try:
189 m.resize(2*mapsize)
190 except SystemError: # resize is not universally supported
191 pass
192 except TypeError:
193 pass
194 else:
195 verify(0, "Able to resize readonly memory map")
196 del m, f
197 verify(open(TESTFN, "rb").read() == 'a'*mapsize,
198 "Readonly memory map data file was modified")
199
Neal Norwitzb5673922002-09-05 21:48:07 +0000200 print " Opening mmap with size too big"
201 import sys
202 f = open(TESTFN, "r+b")
203 try:
204 m = mmap.mmap(f.fileno(), mapsize+1)
205 except ValueError:
206 # we do not expect a ValueError on Windows
Tim Peters4f4f4d72002-09-10 20:49:15 +0000207 # CAUTION: This also changes the size of the file on disk, and
208 # later tests assume that the length hasn't changed. We need to
209 # repair that.
Neal Norwitzb5673922002-09-05 21:48:07 +0000210 if sys.platform.startswith('win'):
211 verify(0, "Opening mmap with size+1 should work on Windows.")
Neal Norwitzb5673922002-09-05 21:48:07 +0000212 else:
213 # we expect a ValueError on Unix, but not on Windows
214 if not sys.platform.startswith('win'):
215 verify(0, "Opening mmap with size+1 should raise ValueError.")
Barry Warsawccd9e752002-09-11 02:56:42 +0000216 m.close()
Tim Peters4f4f4d72002-09-10 20:49:15 +0000217 f.close()
218 if sys.platform.startswith('win'):
219 # Repair damage from the resizing test.
220 f = open(TESTFN, 'r+b')
221 f.truncate(mapsize)
222 f.close()
Neal Norwitzb5673922002-09-05 21:48:07 +0000223
Tim Peters5ebfd362001-11-13 23:11:19 +0000224 print " Opening mmap with access=ACCESS_WRITE"
225 f = open(TESTFN, "r+b")
226 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE)
227 print " Modifying write-through memory map."
228 m[:] = 'c'*mapsize
229 verify(m[:] == 'c'*mapsize,
230 "Write-through memory map memory not updated properly.")
231 m.flush()
Tim Peters1b5112a2002-09-10 21:19:55 +0000232 m.close()
233 f.close()
Tim Peters4f4f4d72002-09-10 20:49:15 +0000234 f = open(TESTFN, 'rb')
235 stuff = f.read()
236 f.close()
Tim Peters1b5112a2002-09-10 21:19:55 +0000237 verify(stuff == 'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000238 "Write-through memory map data file not updated properly.")
239
240 print " Opening mmap with access=ACCESS_COPY"
241 f = open(TESTFN, "r+b")
242 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY)
243 print " Modifying copy-on-write memory map."
244 m[:] = 'd'*mapsize
245 verify(m[:] == 'd' * mapsize,
246 "Copy-on-write memory map data not written correctly.")
247 m.flush()
248 verify(open(TESTFN, "rb").read() == 'c'*mapsize,
249 "Copy-on-write test data file should not be modified.")
250 try:
251 print " Ensuring copy-on-write maps cannot be resized."
252 m.resize(2*mapsize)
253 except TypeError:
254 pass
255 else:
256 verify(0, "Copy-on-write mmap resize did not raise exception.")
257 del m, f
258 try:
259 print " Ensuring invalid access parameter raises exception."
260 f = open(TESTFN, "r+b")
261 m = mmap.mmap(f.fileno(), mapsize, access=4)
262 except ValueError:
263 pass
264 else:
265 verify(0, "Invalid access code should have raised exception.")
266
267 if os.name == "posix":
Tim Peters00cafa02001-11-13 23:39:47 +0000268 # Try incompatible flags, prot and access parameters.
269 f = open(TESTFN, "r+b")
Tim Peters5ebfd362001-11-13 23:11:19 +0000270 try:
271 m = mmap.mmap(f.fileno(), mapsize, flags=mmap.MAP_PRIVATE,
272 prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE)
273 except ValueError:
274 pass
275 else:
276 verify(0, "Incompatible parameters should raise ValueError.")
Tim Peters5379dea2002-04-18 04:30:18 +0000277 f.close()
Tim Peters5ebfd362001-11-13 23:11:19 +0000278 finally:
279 try:
280 os.unlink(TESTFN)
281 except OSError:
282 pass
283
Neal Norwitz3b4fff82006-01-11 08:54:45 +0000284 print ' Try opening a bad file descriptor...'
285 try:
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000286 mmap.mmap(-2, 4096)
Neal Norwitz3b4fff82006-01-11 08:54:45 +0000287 except mmap.error:
288 pass
289 else:
290 verify(0, 'expected a mmap.error but did not get it')
291
Tim Petersc9ffa062002-03-08 05:43:32 +0000292 # Do a tougher .find() test. SF bug 515943 pointed out that, in 2.2,
293 # searching for data with embedded \0 bytes didn't work.
294 f = open(TESTFN, 'w+')
295
296 try: # unlink TESTFN no matter what
297 data = 'aabaac\x00deef\x00\x00aa\x00'
298 n = len(data)
299 f.write(data)
Tim Peters5379dea2002-04-18 04:30:18 +0000300 f.flush()
Tim Petersc9ffa062002-03-08 05:43:32 +0000301 m = mmap.mmap(f.fileno(), n)
302 f.close()
303
304 for start in range(n+1):
305 for finish in range(start, n+1):
306 slice = data[start : finish]
307 vereq(m.find(slice), data.find(slice))
308 vereq(m.find(slice + 'x'), -1)
Tim Petersddc82ea2003-01-13 21:38:45 +0000309 m.close()
Tim Petersc9ffa062002-03-08 05:43:32 +0000310
311 finally:
Tim Petersddc82ea2003-01-13 21:38:45 +0000312 os.unlink(TESTFN)
Tim Petersc9ffa062002-03-08 05:43:32 +0000313
Neal Norwitze604c022003-01-10 20:52:16 +0000314 # make sure a double close doesn't crash on Solaris (Bug# 665913)
315 f = open(TESTFN, 'w+')
316
317 try: # unlink TESTFN no matter what
Tim Petersddc82ea2003-01-13 21:38:45 +0000318 f.write(2**16 * 'a') # Arbitrary character
Neal Norwitze604c022003-01-10 20:52:16 +0000319 f.close()
320
321 f = open(TESTFN)
Tim Petersddc82ea2003-01-13 21:38:45 +0000322 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
Neal Norwitze604c022003-01-10 20:52:16 +0000323 mf.close()
324 mf.close()
325 f.close()
326
327 finally:
Tim Petersddc82ea2003-01-13 21:38:45 +0000328 os.unlink(TESTFN)
Tim Petersc9ffa062002-03-08 05:43:32 +0000329
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000330 # test mapping of entire file by passing 0 for map length
331 if hasattr(os, "stat"):
332 print " Ensuring that passing 0 as map length sets map size to current file size."
333 f = open(TESTFN, "w+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000334
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000335 try:
336 f.write(2**16 * 'm') # Arbitrary character
337 f.close()
338
339 f = open(TESTFN, "rb+")
Tim Peterseba28be2005-03-28 01:08:02 +0000340 mf = mmap.mmap(f.fileno(), 0)
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000341 verify(len(mf) == 2**16, "Map size should equal file size.")
342 vereq(mf.read(2**16), 2**16 * "m")
343 mf.close()
344 f.close()
345
346 finally:
347 os.unlink(TESTFN)
Tim Peterseba28be2005-03-28 01:08:02 +0000348
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000349 # test mapping of entire file by passing 0 for map length
350 if hasattr(os, "stat"):
351 print " Ensuring that passing 0 as map length sets map size to current file size."
352 f = open(TESTFN, "w+")
353 try:
354 f.write(2**16 * 'm') # Arbitrary character
355 f.close()
Tim Peterseba28be2005-03-28 01:08:02 +0000356
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000357 f = open(TESTFN, "rb+")
Tim Peterseba28be2005-03-28 01:08:02 +0000358 mf = mmap.mmap(f.fileno(), 0)
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000359 verify(len(mf) == 2**16, "Map size should equal file size.")
360 vereq(mf.read(2**16), 2**16 * "m")
361 mf.close()
362 f.close()
Tim Peterseba28be2005-03-28 01:08:02 +0000363
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000364 finally:
365 os.unlink(TESTFN)
Tim Peterseba28be2005-03-28 01:08:02 +0000366
Neal Norwitz8856fb72005-12-18 03:34:22 +0000367 # make move works everywhere (64-bit format problem earlier)
368 f = open(TESTFN, 'w+')
369
370 try: # unlink TESTFN no matter what
371 f.write("ABCDEabcde") # Arbitrary character
372 f.flush()
373
374 mf = mmap.mmap(f.fileno(), 10)
375 mf.move(5, 0, 5)
376 verify(mf[:] == "ABCDEABCDE", "Map move should have duplicated front 5")
377 mf.close()
378 f.close()
379
380 finally:
381 os.unlink(TESTFN)
382
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000383def test_anon():
384 print " anonymous mmap.mmap(-1, PAGESIZE)..."
385 m = mmap.mmap(-1, PAGESIZE)
386 for x in xrange(PAGESIZE):
387 verify(m[x] == '\0', "anonymously mmap'ed contents should be zero")
388
389 for x in xrange(PAGESIZE):
390 m[x] = ch = chr(x & 255)
391 vereq(m[x], ch)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000392
Guido van Rossum767e7752000-03-31 01:09:14 +0000393test_both()
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000394test_anon()
395print ' Test passed'