blob: 0b5382307619efc0d288624677f5cc94ec2d7141 [file] [log] [blame]
Thomas Wouters89f507f2006-12-13 04:49:30 +00001from test.test_support import TESTFN, run_unittest
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +00002import mmap
Thomas Wouters89f507f2006-12-13 04:49:30 +00003import unittest
Fred Drake62787992001-05-11 14:29:21 +00004import os, re
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +00005
6PAGESIZE = mmap.PAGESIZE
7
Thomas Wouters89f507f2006-12-13 04:49:30 +00008class MmapTests(unittest.TestCase):
Fred Drake004d5e62000-10-23 17:22:08 +00009
Thomas Wouters89f507f2006-12-13 04:49:30 +000010 def setUp(self):
11 if os.path.exists(TESTFN):
12 os.unlink(TESTFN)
Fred Drake004d5e62000-10-23 17:22:08 +000013
Thomas Wouters89f507f2006-12-13 04:49:30 +000014 def tearDown(self):
Tim Petersfd692082001-05-10 20:03:04 +000015 try:
Fred Drake62787992001-05-11 14:29:21 +000016 os.unlink(TESTFN)
Tim Petersfd692082001-05-10 20:03:04 +000017 except OSError:
18 pass
19
Thomas Wouters89f507f2006-12-13 04:49:30 +000020 def test_basic(self):
21 # Test mmap module on Unix systems and Windows
22
23 # Create a file to be mmap'ed.
24 f = open(TESTFN, 'w+')
25 try:
26 # Write 2 pages worth of data to the file
27 f.write('\0'* PAGESIZE)
28 f.write('foo')
29 f.write('\0'* (PAGESIZE-3) )
30 f.flush()
31 m = mmap.mmap(f.fileno(), 2 * PAGESIZE)
32 f.close()
33
34 # Simple sanity checks
35
36 tp = str(type(m)) # SF bug 128713: segfaulted on Linux
37 self.assertEqual(m.find('foo'), PAGESIZE)
38
39 self.assertEqual(len(m), 2*PAGESIZE)
40
41 self.assertEqual(m[0], '\0')
42 self.assertEqual(m[0:3], '\0\0\0')
43
44 # Modify the file's content
45 m[0] = '3'
46 m[PAGESIZE +3: PAGESIZE +3+3] = 'bar'
47
48 # Check that the modification worked
49 self.assertEqual(m[0], '3')
50 self.assertEqual(m[0:3], '3\0\0')
51 self.assertEqual(m[PAGESIZE-1 : PAGESIZE + 7], '\0foobar\0')
52
53 m.flush()
54
55 # Test doing a regular expression match in an mmap'ed file
56 match = re.search('[A-Za-z]+', m)
57 if match is None:
58 self.fail('regex match on mmap failed!')
59 else:
60 start, end = match.span(0)
61 length = end - start
62
63 self.assertEqual(start, PAGESIZE)
64 self.assertEqual(end, PAGESIZE + 6)
65
66 # test seeking around (try to overflow the seek implementation)
67 m.seek(0,0)
68 self.assertEqual(m.tell(), 0)
69 m.seek(42,1)
70 self.assertEqual(m.tell(), 42)
71 m.seek(0,2)
72 self.assertEqual(m.tell(), len(m))
73
74 # Try to seek to negative position...
75 self.assertRaises(ValueError, m.seek, -1)
76
77 # Try to seek beyond end of mmap...
78 self.assertRaises(ValueError, m.seek, 1, 2)
79
80 # Try to seek to negative position...
81 self.assertRaises(ValueError, m.seek, -len(m)-1, 2)
82
83 # Try resizing map
84 try:
85 m.resize(512)
86 except SystemError:
87 # resize() not supported
88 # No messages are printed, since the output of this test suite
89 # would then be different across platforms.
90 pass
91 else:
92 # resize() is supported
93 self.assertEqual(len(m), 512)
94 # Check that we can no longer seek beyond the new size.
95 self.assertRaises(ValueError, m.seek, 513, 0)
96
97 # Check that the underlying file is truncated too
98 # (bug #728515)
99 f = open(TESTFN)
100 f.seek(0, 2)
101 self.assertEqual(f.tell(), 512)
102 f.close()
103 self.assertEqual(m.size(), 512)
104
105 m.close()
106
107 finally:
108 try:
109 f.close()
110 except OSError:
111 pass
112
113 def test_access_parameter(self):
114 # Test for "access" keyword parameter
Tim Peters5ebfd362001-11-13 23:11:19 +0000115 mapsize = 10
Tim Peters5ebfd362001-11-13 23:11:19 +0000116 open(TESTFN, "wb").write("a"*mapsize)
Tim Peters5ebfd362001-11-13 23:11:19 +0000117 f = open(TESTFN, "rb")
118 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_READ)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000119 self.assertEqual(m[:], 'a'*mapsize, "Readonly memory map data incorrect.")
Tim Peters5ebfd362001-11-13 23:11:19 +0000120
Thomas Wouters89f507f2006-12-13 04:49:30 +0000121 # Ensuring that readonly mmap can't be slice assigned
Tim Peters5ebfd362001-11-13 23:11:19 +0000122 try:
123 m[:] = 'b'*mapsize
124 except TypeError:
125 pass
126 else:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000127 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000128
Thomas Wouters89f507f2006-12-13 04:49:30 +0000129 # Ensuring that readonly mmap can't be item assigned
Tim Peters5ebfd362001-11-13 23:11:19 +0000130 try:
131 m[0] = 'b'
132 except TypeError:
133 pass
134 else:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000135 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000136
Thomas Wouters89f507f2006-12-13 04:49:30 +0000137 # Ensuring that readonly mmap can't be write() to
Tim Peters5ebfd362001-11-13 23:11:19 +0000138 try:
139 m.seek(0,0)
140 m.write('abc')
141 except TypeError:
142 pass
143 else:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000144 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000145
Thomas Wouters89f507f2006-12-13 04:49:30 +0000146 # Ensuring that readonly mmap can't be write_byte() to
Tim Peters5ebfd362001-11-13 23:11:19 +0000147 try:
148 m.seek(0,0)
149 m.write_byte('d')
150 except TypeError:
151 pass
152 else:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000153 self.fail("Able to write to readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000154
Thomas Wouters89f507f2006-12-13 04:49:30 +0000155 # Ensuring that readonly mmap can't be resized
Tim Peters5ebfd362001-11-13 23:11:19 +0000156 try:
157 m.resize(2*mapsize)
158 except SystemError: # resize is not universally supported
159 pass
160 except TypeError:
161 pass
162 else:
Thomas Wouters89f507f2006-12-13 04:49:30 +0000163 self.fail("Able to resize readonly memory map")
Tim Peters5ebfd362001-11-13 23:11:19 +0000164 del m, f
Thomas Wouters89f507f2006-12-13 04:49:30 +0000165 self.assertEqual(open(TESTFN, "rb").read(), 'a'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000166 "Readonly memory map data file was modified")
167
Thomas Wouters89f507f2006-12-13 04:49:30 +0000168 # Opening mmap with size too big
Neal Norwitzb5673922002-09-05 21:48:07 +0000169 import sys
170 f = open(TESTFN, "r+b")
171 try:
172 m = mmap.mmap(f.fileno(), mapsize+1)
173 except ValueError:
174 # we do not expect a ValueError on Windows
Tim Peters4f4f4d72002-09-10 20:49:15 +0000175 # CAUTION: This also changes the size of the file on disk, and
176 # later tests assume that the length hasn't changed. We need to
177 # repair that.
Neal Norwitzb5673922002-09-05 21:48:07 +0000178 if sys.platform.startswith('win'):
Thomas Wouters89f507f2006-12-13 04:49:30 +0000179 self.fail("Opening mmap with size+1 should work on Windows.")
Neal Norwitzb5673922002-09-05 21:48:07 +0000180 else:
181 # we expect a ValueError on Unix, but not on Windows
182 if not sys.platform.startswith('win'):
Thomas Wouters89f507f2006-12-13 04:49:30 +0000183 self.fail("Opening mmap with size+1 should raise ValueError.")
Barry Warsawccd9e752002-09-11 02:56:42 +0000184 m.close()
Tim Peters4f4f4d72002-09-10 20:49:15 +0000185 f.close()
186 if sys.platform.startswith('win'):
187 # Repair damage from the resizing test.
188 f = open(TESTFN, 'r+b')
189 f.truncate(mapsize)
190 f.close()
Neal Norwitzb5673922002-09-05 21:48:07 +0000191
Thomas Wouters89f507f2006-12-13 04:49:30 +0000192 # Opening mmap with access=ACCESS_WRITE
Tim Peters5ebfd362001-11-13 23:11:19 +0000193 f = open(TESTFN, "r+b")
194 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_WRITE)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000195 # Modifying write-through memory map
Tim Peters5ebfd362001-11-13 23:11:19 +0000196 m[:] = 'c'*mapsize
Thomas Wouters89f507f2006-12-13 04:49:30 +0000197 self.assertEqual(m[:], 'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000198 "Write-through memory map memory not updated properly.")
199 m.flush()
Tim Peters1b5112a2002-09-10 21:19:55 +0000200 m.close()
201 f.close()
Tim Peters4f4f4d72002-09-10 20:49:15 +0000202 f = open(TESTFN, 'rb')
203 stuff = f.read()
204 f.close()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000205 self.assertEqual(stuff, 'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000206 "Write-through memory map data file not updated properly.")
207
Thomas Wouters89f507f2006-12-13 04:49:30 +0000208 # Opening mmap with access=ACCESS_COPY
Tim Peters5ebfd362001-11-13 23:11:19 +0000209 f = open(TESTFN, "r+b")
210 m = mmap.mmap(f.fileno(), mapsize, access=mmap.ACCESS_COPY)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000211 # Modifying copy-on-write memory map
Tim Peters5ebfd362001-11-13 23:11:19 +0000212 m[:] = 'd'*mapsize
Thomas Wouters89f507f2006-12-13 04:49:30 +0000213 self.assertEqual(m[:], 'd' * mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000214 "Copy-on-write memory map data not written correctly.")
215 m.flush()
Thomas Wouters89f507f2006-12-13 04:49:30 +0000216 self.assertEqual(open(TESTFN, "rb").read(), 'c'*mapsize,
Tim Peters5ebfd362001-11-13 23:11:19 +0000217 "Copy-on-write test data file should not be modified.")
Thomas Wouters89f507f2006-12-13 04:49:30 +0000218 # Ensuring copy-on-write maps cannot be resized
219 self.assertRaises(TypeError, m.resize, 2*mapsize)
Tim Peters5ebfd362001-11-13 23:11:19 +0000220 del m, f
Thomas Wouters89f507f2006-12-13 04:49:30 +0000221
222 # Ensuring invalid access parameter raises exception
223 f = open(TESTFN, "r+b")
224 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize, access=4)
225 f.close()
Tim Peters5ebfd362001-11-13 23:11:19 +0000226
227 if os.name == "posix":
Tim Peters00cafa02001-11-13 23:39:47 +0000228 # Try incompatible flags, prot and access parameters.
229 f = open(TESTFN, "r+b")
Thomas Wouters89f507f2006-12-13 04:49:30 +0000230 self.assertRaises(ValueError, mmap.mmap, f.fileno(), mapsize,
231 flags=mmap.MAP_PRIVATE,
Tim Peters5ebfd362001-11-13 23:11:19 +0000232 prot=mmap.PROT_READ, access=mmap.ACCESS_WRITE)
Tim Peters5379dea2002-04-18 04:30:18 +0000233 f.close()
Tim Peters5ebfd362001-11-13 23:11:19 +0000234
Thomas Wouters89f507f2006-12-13 04:49:30 +0000235 def test_bad_file_desc(self):
236 # Try opening a bad file descriptor...
237 self.assertRaises(mmap.error, mmap.mmap, -2, 4096)
Neal Norwitz3b4fff82006-01-11 08:54:45 +0000238
Thomas Wouters89f507f2006-12-13 04:49:30 +0000239 def test_tougher_find(self):
240 # Do a tougher .find() test. SF bug 515943 pointed out that, in 2.2,
241 # searching for data with embedded \0 bytes didn't work.
242 f = open(TESTFN, 'w+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000243
Tim Petersc9ffa062002-03-08 05:43:32 +0000244 data = 'aabaac\x00deef\x00\x00aa\x00'
245 n = len(data)
246 f.write(data)
Tim Peters5379dea2002-04-18 04:30:18 +0000247 f.flush()
Tim Petersc9ffa062002-03-08 05:43:32 +0000248 m = mmap.mmap(f.fileno(), n)
249 f.close()
250
251 for start in range(n+1):
252 for finish in range(start, n+1):
253 slice = data[start : finish]
Thomas Wouters89f507f2006-12-13 04:49:30 +0000254 self.assertEqual(m.find(slice), data.find(slice))
255 self.assertEqual(m.find(slice + 'x'), -1)
Tim Petersddc82ea2003-01-13 21:38:45 +0000256 m.close()
Tim Petersc9ffa062002-03-08 05:43:32 +0000257
Thomas Wouters89f507f2006-12-13 04:49:30 +0000258 def test_double_close(self):
259 # make sure a double close doesn't crash on Solaris (Bug# 665913)
260 f = open(TESTFN, 'w+')
Tim Petersc9ffa062002-03-08 05:43:32 +0000261
Tim Petersddc82ea2003-01-13 21:38:45 +0000262 f.write(2**16 * 'a') # Arbitrary character
Neal Norwitze604c022003-01-10 20:52:16 +0000263 f.close()
264
265 f = open(TESTFN)
Tim Petersddc82ea2003-01-13 21:38:45 +0000266 mf = mmap.mmap(f.fileno(), 2**16, access=mmap.ACCESS_READ)
Neal Norwitze604c022003-01-10 20:52:16 +0000267 mf.close()
268 mf.close()
269 f.close()
270
Thomas Wouters89f507f2006-12-13 04:49:30 +0000271 def test_entire_file(self):
272 # test mapping of entire file by passing 0 for map length
273 if hasattr(os, "stat"):
274 f = open(TESTFN, "w+")
Tim Petersc9ffa062002-03-08 05:43:32 +0000275
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000276 f.write(2**16 * 'm') # Arbitrary character
277 f.close()
278
279 f = open(TESTFN, "rb+")
Tim Peterseba28be2005-03-28 01:08:02 +0000280 mf = mmap.mmap(f.fileno(), 0)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000281 self.assertEqual(len(mf), 2**16, "Map size should equal file size.")
282 self.assertEqual(mf.read(2**16), 2**16 * "m")
Martin v. Löwis7fe60c02005-03-03 11:22:44 +0000283 mf.close()
284 f.close()
285
Thomas Wouters89f507f2006-12-13 04:49:30 +0000286 def test_move(self):
287 # make move works everywhere (64-bit format problem earlier)
288 f = open(TESTFN, 'w+')
Tim Peterseba28be2005-03-28 01:08:02 +0000289
Neal Norwitz8856fb72005-12-18 03:34:22 +0000290 f.write("ABCDEabcde") # Arbitrary character
291 f.flush()
292
293 mf = mmap.mmap(f.fileno(), 10)
294 mf.move(5, 0, 5)
Thomas Wouters89f507f2006-12-13 04:49:30 +0000295 self.assertEqual(mf[:], "ABCDEABCDE", "Map move should have duplicated front 5")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000296 mf.close()
297 f.close()
298
Thomas Wouters89f507f2006-12-13 04:49:30 +0000299 def test_anonymous(self):
300 # anonymous mmap.mmap(-1, PAGE)
301 m = mmap.mmap(-1, PAGESIZE)
302 for x in xrange(PAGESIZE):
303 self.assertEqual(m[x], '\0', "anonymously mmap'ed contents should be zero")
Neal Norwitz8856fb72005-12-18 03:34:22 +0000304
Thomas Wouters89f507f2006-12-13 04:49:30 +0000305 for x in xrange(PAGESIZE):
306 m[x] = ch = chr(x & 255)
307 self.assertEqual(m[x], ch)
Neal Norwitz0e6bc8c2006-02-05 05:45:43 +0000308
Thomas Wouters89f507f2006-12-13 04:49:30 +0000309def test_main():
310 run_unittest(MmapTests)
Andrew M. Kuchlinge81b9cf2000-03-30 21:15:29 +0000311
Thomas Wouters89f507f2006-12-13 04:49:30 +0000312if __name__ == '__main__':
313 test_main()