9c273b84a596e848680054d9889147050a6b2530
[mono.git] / mcs / class / System.Core / Test / System.IO.MemoryMappedFiles / MemoryMappedFileTest.cs
1 //
2 // MemoryMappedFileTest.cs
3 //
4 // Author:
5 //   Zoltan Varga (vargaz@gmail.com)
6 //
7 // (C) 2009 Novell, Inc. (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27 //
28
29
30 using System;
31 using System.IO;
32 using System.IO.MemoryMappedFiles;
33 using System.Linq;
34
35 using NUnit.Framework;
36
37 namespace MonoTests.System.IO.MemoryMappedFiles {
38
39         [TestFixture]
40         public class MemoryMappedFileTest {
41
42                 void AssertThrows<ExType> (Action del) where ExType : Exception {
43                         bool thrown = false;
44
45                         try {
46                                 del ();
47                         } catch (ExType) {
48                                 thrown = true;
49                         }
50                         Assert.IsTrue (thrown);
51                 }
52
53                 static int named_index;
54                 static String MkNamedMapping ()
55                 {
56                         return "test-" + named_index++;
57                 }
58
59                 static string baseTempDir = Path.Combine (Path.GetTempPath (), typeof (MemoryMappedFileTest).FullName);
60                 static string tempDir = Path.Combine (Path.GetTempPath (), typeof (MemoryMappedFileTest).FullName);
61
62                 string fname;
63
64                 [TestFixtureSetUp]
65                 public void FixtureSetUp ()
66                 {
67                         try {
68                                 // Try to cleanup from any previous NUnit run.
69                                 Directory.Delete (baseTempDir, true);
70                         } catch (Exception) {
71                         }
72                 }
73
74                 [SetUp]
75                 public void SetUp ()
76                 {
77                         int i = 0;
78                         do {
79                                 tempDir = Path.Combine (baseTempDir, (++i).ToString());
80                         } while (Directory.Exists (tempDir));
81                         Directory.CreateDirectory (tempDir);
82
83                         fname = Path.Combine (tempDir, "basic.txt");
84
85                         using (StreamWriter sw = new StreamWriter (fname)) {
86                                 sw.WriteLine ("Hello!");
87                                 sw.WriteLine ("World!");
88                         }
89                 }
90
91                 [TearDown]
92                 public void TearDown ()
93                 {
94                         try {
95                                 // This throws an exception under MS.NET and Mono on Windows,
96                                 // since the directory contains open files.
97                                 Directory.Delete (tempDir, true);
98                         } catch (Exception) {
99                         }
100                 }
101
102                 [Test]
103                 public void Basic () {
104                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
105
106                         using (var stream = file.CreateViewStream ()) {
107                                 TextReader r = new StreamReader (stream);
108
109                                 string s;
110
111                                 s = r.ReadLine ();
112                                 Assert.AreEqual ("Hello!", s);
113                                 s = r.ReadLine ();
114                                 Assert.AreEqual ("World!", s);
115                         }
116                 }
117
118                 [Test]
119                 public void CreateNew ()
120                 {
121                         // This must succeed
122                         MemoryMappedFile.CreateNew (MkNamedMapping (), 8192);
123                 }
124
125                 // Call this twice, it should always work
126                 [Test]
127                 public void CreateOrOpen_Multiple ()
128                 {
129                         var name = MkNamedMapping ();
130                         MemoryMappedFile.CreateOrOpen (name, 8192);
131                         MemoryMappedFile.CreateOrOpen (name, 8192);
132                 }
133
134                 [Test]
135                 [ExpectedException(typeof(ArgumentOutOfRangeException))]
136                 public void CreateFromFileWithSmallerCapacityThanFile ()
137                 {
138                         var f = Path.Combine (tempDir, "8192-file");
139                         File.WriteAllBytes (f, new byte [8192]);
140
141                         // We are requesting fewer bytes to map.
142                         MemoryMappedFile.CreateFromFile (f, FileMode.Open, "myMap", 4192);
143                 }
144
145                 [Test]
146                 public void CreateFromFile_Null () {
147                         AssertThrows<ArgumentNullException> (delegate () {
148                                         MemoryMappedFile.CreateFromFile (null);
149                                 });
150                 }
151
152                 [Test]
153                 public void CreateViewStream_Offsets () {
154                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
155
156                         using (var stream = file.CreateViewStream (2, 3)) {
157                                 byte[] arr = new byte [128];
158
159                                 int len = stream.Read (arr, 0, 128);
160
161                                 Assert.AreEqual (3, len);
162
163                                 Assert.AreEqual ('l', (char)arr [0]);
164                                 Assert.AreEqual ('l', (char)arr [1]);
165                                 Assert.AreEqual ('o', (char)arr [2]);
166                         }
167                 }
168
169                 [Test]
170                 public void CreateViewStream_Rights () {
171                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
172
173                         using (var stream = file.CreateViewStream (0, 0, MemoryMappedFileAccess.Read)) {
174                                 AssertThrows<NotSupportedException> (delegate () {
175                                                 stream.WriteByte (0);
176                                         });
177                         }
178
179                         using (var stream = file.CreateViewStream (0, 0, MemoryMappedFileAccess.Write)) {
180                                 AssertThrows<NotSupportedException> (delegate () {
181                                                 stream.ReadByte ();
182                                         });
183                         }
184                 }
185
186                 [Test]
187                 public unsafe void CreateViewBasic () {
188                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
189
190                         using (var v = file.CreateViewAccessor ()) {
191                                 string s = "";
192
193                                 // FIXME: Use using
194                                 var handle = v.SafeMemoryMappedViewHandle;
195                                 byte *b = null;
196
197                                 try {
198                                         handle.AcquirePointer (ref b);
199
200                                         for (int i = 0; i < 5; ++i)
201                                                 s += (char)b [i];
202                                 } finally {
203                                         handle.ReleasePointer ();
204                                 }
205
206                                 Assert.AreEqual ("Hello", s);
207                         }
208                 }
209
210                 [Test]
211                 public unsafe void ViewReadArray () {
212                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
213
214                         using (var v = file.CreateViewAccessor ()) {
215                                 var a = new byte [5];
216                                 var n = v.ReadArray (0, a, 0, 5);
217                                 Assert.AreEqual (5, n);
218                                 var s = new string (Array.ConvertAll (a, b => (char)b));
219                                 Assert.AreEqual ("Hello", s);
220                         }
221                 }
222
223                 [Test]
224                 public unsafe void ViewAccessorReadArrayWithOffset () {
225                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
226                         var offset = 3;
227                         var expected = "lo!";
228
229                         using (var v = file.CreateViewAccessor (offset, expected.Length)) {
230                                 // PointerOffset Mono implementation is always 0.
231                                 // Assert.AreEqual (offset, v.PointerOffset);
232
233                                 var a = new byte [expected.Length];
234                                 var n = v.ReadArray (0, a, 0, expected.Length);
235                                 Assert.AreEqual (expected.Length, n);
236                                 var s = new string (Array.ConvertAll (a, b => (char)b));
237                                 Assert.AreEqual (expected, s);
238                         }
239                 }
240
241                 [Test]
242                 public unsafe void ViewStreamReadWithOffset () {
243                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
244                         var offset = 3;
245                         var expected = "lo!";
246
247                         using (var v = file.CreateViewStream (offset, expected.Length)) {
248                                 // PointerOffset Mono implementation is always 0.
249                                 // Assert.AreEqual (offset, v.PointerOffset);
250
251                                 var a = new byte [expected.Length];
252                                 var n = v.Read (a, 0, expected.Length);
253                                 Assert.AreEqual (expected.Length, n);
254                                 var s = new string (Array.ConvertAll (a, b => (char)b));
255                                 Assert.AreEqual (expected, s);
256                         }
257                 }
258
259                 [Test]
260                 public void NamedMappingToInvalidFile ()
261                 {
262                         if (Environment.OSVersion.Platform != PlatformID.Unix) {
263                                 Assert.Ignore ("Backslashes in mapping names are disallowed on .NET and Mono on Windows");
264                         }
265                         var fileName = Path.Combine (tempDir, "temp_file_123");
266                 if (File.Exists (fileName))
267                     File.Delete (fileName);
268                 var memoryMappedFile90 = MemoryMappedFile.CreateNew (fileName, 4194304, MemoryMappedFileAccess.ReadWrite);
269                 memoryMappedFile90.CreateViewStream (4186112, 3222, MemoryMappedFileAccess.Write);
270                 }
271
272                 [Test]
273                 public void CreateTheSameAreaTwiceShouldFail ()
274                 {
275                         var name = MkNamedMapping ();
276                         using (var m0 = MemoryMappedFile.CreateNew(name, 4096, MemoryMappedFileAccess.ReadWrite)) {
277                                 try {
278                                         using (var m1 = MemoryMappedFile.CreateNew (name, 4096, MemoryMappedFileAccess.ReadWrite)) {
279                                                 Assert.Fail ("Must fail");
280                                         }
281                                 } catch (IOException) {}
282                         }
283                 }
284
285                 [Test]
286                 public void MapAFileToAMemoryAreaShouldFail ()
287                 {
288                         var name = MkNamedMapping ();
289                         using (var m0 = MemoryMappedFile.CreateNew(name, 4096, MemoryMappedFileAccess.ReadWrite)) {
290                                 try {
291                                         using (var m1 = MemoryMappedFile.CreateFromFile (fname, FileMode.OpenOrCreate, name)) {
292                                                 Assert.Fail ("Must fail");
293                                         }
294                                 } catch (IOException) {}
295                         }
296                 }
297
298                 [Test]
299                 public void NamedMappingsShareMemoryArea ()
300                 {
301                         var name = MkNamedMapping ();
302                         using (var m0 = MemoryMappedFile.CreateNew(name, 4096, MemoryMappedFileAccess.ReadWrite)) {
303                                 using (var m1 = MemoryMappedFile.CreateOrOpen (name, 4096, MemoryMappedFileAccess.ReadWrite)) {
304                                         using (MemoryMappedViewAccessor v0 = m0.CreateViewAccessor (), v1 = m1.CreateViewAccessor ()) {
305                                                 v0.Write (10, 0x12345);
306                                                 Assert.AreEqual (0x12345, v1.ReadInt32 (10));
307                                         }
308                                 }
309                         }
310                 }
311
312                 [Test]
313                 public void NamedFileCanBeOpen ()
314                 {
315                         var name = MkNamedMapping ();
316                         using (var sw = new FileStream (fname, FileMode.Open)) {
317                                 byte[] b = new byte[20];
318                                 for (int i = 0; i < 20; ++i)
319                                         b[i] = 0xFF;
320                                 sw.Write (b, 0, 20);
321                         }
322
323                         using (var m0 = MemoryMappedFile.CreateFromFile (fname, FileMode.Open, name)) {
324                                 using (var m1 = MemoryMappedFile.CreateOrOpen (name, 4096)) {
325                                         using (MemoryMappedViewAccessor v0 = m0.CreateViewAccessor (), v1 = m1.CreateViewAccessor ()) {
326                                                 v0.Write (10, 0x11223344);
327                                                 Assert.AreEqual (0x11223344, v1.ReadInt32 (10));
328                                         }
329                                 }
330                         }
331                 }
332
333                 [Test]
334                 public void MapAtEdgeOfPage ()
335                 {
336                         using (var f = new FileStream (fname, FileMode.Open)) {
337                                 var b = new byte [4096];
338                                 for (int i = 0; i < 4096; ++i)
339                                         b[i] = 0xAA;
340                                 for (int i = 0; i < 2; ++i)
341                                         f.Write (b, 0, 4096);
342                         }
343                         var m0 = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
344                         var v0 = m0.CreateViewAccessor (500, 4096);
345                         var v1 = m0.CreateViewAccessor (0, 4096 * 2);
346                         for (int i = 0; i < 4096; ++i) {
347                                 Assert.AreEqual (0xAA, v1.ReadByte (i + 500));
348                                 v0.Write (i, (byte)0xFF);
349                                 Assert.AreEqual (0xFF, v1.ReadByte (i + 500));
350                         }
351                 }
352
353                 [Test]
354                 public void DoubleAccountingInOffsetCalculation ()
355                 {
356                         var memoryMappedFile90 = MemoryMappedFile.CreateNew (MkNamedMapping (), 4194304, MemoryMappedFileAccess.ReadWrite);
357                         var stream = memoryMappedFile90.CreateViewStream (4186112, 3222, MemoryMappedFileAccess.Write);
358                         using (var tw = new StreamWriter(stream))
359                         {
360                                 tw.WriteLine ("Hello World!");
361                         }
362                 }
363
364                 [Test]
365                 [ExpectedException(typeof(UnauthorizedAccessException))]
366                 public void CreateViewStreamWithOffsetPastFileEnd ()
367                 {
368                         string f = Path.Combine (tempDir, "8192-file");
369                         File.WriteAllBytes (f, new byte [8192]);
370
371                         MemoryMappedFile mappedFile = MemoryMappedFile.CreateFromFile (f, FileMode.Open, "myMap", 8192);
372
373                         /* Should throw exception when trying to map past end of file */
374                         MemoryMappedViewStream stream = mappedFile.CreateViewStream (8200, 10, MemoryMappedFileAccess.ReadWrite);
375                 }
376
377                 [Test]
378                 [ExpectedException(typeof(UnauthorizedAccessException))]
379                 public void CreateViewStreamWithOffsetPastFileEnd2 ()
380                 {
381                         string f = Path.Combine (tempDir, "8192-file");
382                         File.WriteAllBytes (f, new byte [8192]);
383
384                         MemoryMappedFile mappedFile = MemoryMappedFile.CreateFromFile (f, FileMode.Open);
385
386                         MemoryMappedViewStream stream = mappedFile.CreateViewStream (8191, 8191, MemoryMappedFileAccess.ReadWrite);
387                 }
388
389                 [Test]
390                 public void CreateViewStreamAlignToPageSize ()
391                 {
392 #if __WATCHOS__
393                         int pageSize = 4096;
394 #elif MONOTOUCH
395                         // iOS bugs on ARM64 - bnc #27667 - apple #
396                         int pageSize = (IntPtr.Size == 4) ? Environment.SystemPageSize : 4096;
397 #else
398                         int pageSize = Environment.SystemPageSize;
399 #endif
400                         string f = Path.Combine (tempDir, "p-file");
401                         File.WriteAllBytes (f, new byte [pageSize * 2 + 1]);
402
403                         MemoryMappedFile mappedFile = MemoryMappedFile.CreateFromFile (f, FileMode.Open);
404
405                         MemoryMappedViewStream stream = mappedFile.CreateViewStream (pageSize * 2, 0, MemoryMappedFileAccess.ReadWrite);
406 #if !MONOTOUCH
407                         Assert.AreEqual (Environment.SystemPageSize, stream.Capacity);
408 #endif
409                         stream.Write (new byte [pageSize], 0, pageSize);
410                 }
411
412                 [Test] // #30741 #30825
413                 public void CreateFromFileNullMapName ()
414                 {
415                         int size = 100;
416                         string f = Path.Combine (tempDir, "null-map-name-file");
417                         File.WriteAllBytes (f, new byte [size]);
418
419                         FileStream file = File.OpenRead (f);
420                         MemoryMappedFile.CreateFromFile (file, null, size, MemoryMappedFileAccess.Read, null, 0, false);
421                 }
422
423                 [Test]
424                 [ExpectedException(typeof(ArgumentOutOfRangeException))]
425                 public void CreateNewLargerThanLogicalAddressSpace ()
426                 {
427                         if (IntPtr.Size != 4) {
428                                 Assert.Ignore ("Only applies to 32-bit systems");
429                         }
430                         MemoryMappedFile.CreateNew (MkNamedMapping (), (long) uint.MaxValue + 1);
431                 }
432
433                 [Test]
434                 [ExpectedException(typeof(ArgumentOutOfRangeException))]
435                 public void CreateOrOpenLargerThanLogicalAddressSpace ()
436                 {
437                         if (IntPtr.Size != 4) {
438                                 Assert.Ignore ("Only applies to 32-bit systems");
439                         }
440                         MemoryMappedFile.CreateOrOpen (MkNamedMapping (), (long) uint.MaxValue + 1);
441                 }
442
443                 [Test]
444                 public void NamedMappingWithDelayAllocatePages ()
445                 {
446                         var name = MkNamedMapping ();
447                         using (var m0 = MemoryMappedFile.CreateNew(name, 4096, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.DelayAllocatePages, HandleInheritability.None)) {
448                                 using (MemoryMappedViewAccessor v0 = m0.CreateViewAccessor ()) {
449                                         Assert.AreEqual (0, v0.ReadInt32 (0));
450                                 }
451                         }
452                 }
453
454         }
455 }