Merge pull request #1936 from esdrubal/DotNetRelativeOrAbsolute
[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
60                 static string tempDir = Path.Combine (Path.GetTempPath (), typeof (MemoryMappedFileTest).FullName);
61
62                 string fname;
63
64                 [SetUp]
65                 protected void SetUp () {
66                         if (Directory.Exists (tempDir))
67                                 Directory.Delete (tempDir, true);
68
69                         Directory.CreateDirectory (tempDir);
70
71                         fname = Path.Combine (tempDir, "basic.txt");
72
73                         using (StreamWriter sw = new StreamWriter (fname)) {
74                                 sw.WriteLine ("Hello!");
75                                 sw.WriteLine ("World!");
76                         }
77                 }
78
79                 [TearDown]
80                 protected void TearDown () {
81                         if (Directory.Exists (tempDir))
82                                 Directory.Delete (tempDir, true);
83                 }
84
85                 [Test]
86                 public void Basic () {
87                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
88
89                         using (var stream = file.CreateViewStream ()) {
90                                 TextReader r = new StreamReader (stream);
91
92                                 string s;
93
94                                 s = r.ReadLine ();
95                                 Assert.AreEqual ("Hello!", s);
96                                 s = r.ReadLine ();
97                                 Assert.AreEqual ("World!", s);
98                         }
99                 }
100
101                 [Test]
102                 public void CreateNew ()
103                 {
104                         // This must succeed
105                         MemoryMappedFile.CreateNew (Path.Combine (tempDir, "createNew.test"), 8192);
106                 }
107
108                 [Test]
109                 [ExpectedException (typeof (IOException))]
110                 public void CreateNew_OnExistingFile ()
111                 {
112                         // This must succeed
113                         MemoryMappedFile.CreateNew (Path.Combine (tempDir, "createNew.test"), 8192);
114                         
115                         // This should fail, the file exists
116                         MemoryMappedFile.CreateNew (Path.Combine (tempDir, "createNew.test"), 8192);
117                 }
118
119                 // Call this twice, it should always work
120                 [Test]
121                 public void CreateOrOpen_Multiple ()
122                 {
123                         MemoryMappedFile.CreateOrOpen (Path.Combine (tempDir, "createOrOpen.test"), 8192);
124                         MemoryMappedFile.CreateOrOpen (Path.Combine (tempDir, "createOrOpen.test"), 8192);
125                 }
126
127                 [Test]
128                 [ExpectedException(typeof(ArgumentOutOfRangeException))]
129                 public void CreateFromFileWithSmallerCapacityThanFile ()
130                 {
131                         var f = Path.Combine (tempDir, "8192-file");
132                         File.WriteAllBytes (f, new byte [8192]);
133
134                         // We are requesting fewer bytes to map.
135                         MemoryMappedFile.CreateFromFile (f, FileMode.Open, "myMap", 4192);
136                 }
137         
138                 [Test]
139                 public void CreateFromFile_Null () {
140                         AssertThrows<ArgumentNullException> (delegate () {
141                                         MemoryMappedFile.CreateFromFile (null);
142                                 });
143                 }
144
145                 [Test]
146                 public void CreateViewStream_Offsets () {
147                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
148
149                         using (var stream = file.CreateViewStream (2, 3)) {
150                                 byte[] arr = new byte [128];
151
152                                 int len = stream.Read (arr, 0, 128);
153
154                                 Assert.AreEqual (3, len);
155
156                                 Assert.AreEqual ('l', (char)arr [0]);
157                                 Assert.AreEqual ('l', (char)arr [1]);
158                                 Assert.AreEqual ('o', (char)arr [2]);
159                         }
160                 }
161
162                 [Test]
163                 public void CreateViewStream_Rights () {
164                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
165
166                         using (var stream = file.CreateViewStream (0, 0, MemoryMappedFileAccess.Read)) {
167                                 AssertThrows<NotSupportedException> (delegate () {
168                                                 stream.WriteByte (0);
169                                         });
170                         }
171
172                         using (var stream = file.CreateViewStream (0, 0, MemoryMappedFileAccess.Write)) {
173                                 AssertThrows<NotSupportedException> (delegate () {
174                                                 stream.ReadByte ();
175                                         });
176                         }
177                 }
178
179                 [Test]
180                 public unsafe void CreateViewBasic () {
181                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
182
183                         using (var v = file.CreateViewAccessor ()) {
184                                 string s = "";
185
186                                 // FIXME: Use using
187                                 var handle = v.SafeMemoryMappedViewHandle;
188                                 byte *b = null;
189
190                                 try {
191                                         handle.AcquirePointer (ref b);
192
193                                         for (int i = 0; i < 5; ++i)
194                                                 s += (char)b [i];
195                                 } finally {
196                                         handle.ReleasePointer ();
197                                 }
198
199                                 Assert.AreEqual ("Hello", s);
200                         }
201                 }
202
203                 [Test]
204                 public unsafe void ViewReadArray () {
205                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
206
207                         using (var v = file.CreateViewAccessor ()) {
208                                 var a = new byte [5];
209                                 var n = v.ReadArray (0, a, 0, 5);
210                                 Assert.AreEqual (5, n);
211                                 var s = new string (Array.ConvertAll (a, b => (char)b));
212                                 Assert.AreEqual ("Hello", s);
213                         }
214                 }
215
216                 [Test]
217                 public unsafe void ViewAccessorReadArrayWithOffset () {
218                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
219                         var offset = 3;
220                         var expected = "lo!";
221
222                         using (var v = file.CreateViewAccessor (offset, expected.Length)) {
223                                 // PointerOffset Mono implementation is always 0.
224                                 // Assert.AreEqual (offset, v.PointerOffset);
225
226                                 var a = new byte [expected.Length];
227                                 var n = v.ReadArray (0, a, 0, expected.Length);
228                                 Assert.AreEqual (expected.Length, n);
229                                 var s = new string (Array.ConvertAll (a, b => (char)b));
230                                 Assert.AreEqual (expected, s);
231                         }
232                 }
233
234                 [Test]
235                 public unsafe void ViewStreamReadWithOffset () {
236                         var file = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
237                         var offset = 3;
238                         var expected = "lo!";
239
240                         using (var v = file.CreateViewStream (offset, expected.Length)) {
241                                 // PointerOffset Mono implementation is always 0.
242                                 // Assert.AreEqual (offset, v.PointerOffset);
243
244                                 var a = new byte [expected.Length];
245                                 var n = v.Read (a, 0, expected.Length);
246                                 Assert.AreEqual (expected.Length, n);
247                                 var s = new string (Array.ConvertAll (a, b => (char)b));
248                                 Assert.AreEqual (expected, s);
249                         }
250                 }
251
252                 [Test]
253                 public void NamedMappingToInvalidFile ()
254                 {
255                         var fileName = Path.Combine (tempDir, "temp_file_123");
256                 if (File.Exists (fileName))
257                     File.Delete (fileName);
258                 var memoryMappedFile90 = MemoryMappedFile.CreateNew (fileName, 4194304, MemoryMappedFileAccess.ReadWrite);
259                 memoryMappedFile90.CreateViewStream (4186112, 3222, MemoryMappedFileAccess.Write);
260                 }
261
262                 [Test]
263                 public void CreateTheSameAreaTwiceShouldFail ()
264                 {
265                         var name = MkNamedMapping ();
266                         using (var m0 = MemoryMappedFile.CreateNew(name, 4096, MemoryMappedFileAccess.ReadWrite)) {
267                                 try {
268                                         using (var m1 = MemoryMappedFile.CreateNew (name, 4096, MemoryMappedFileAccess.ReadWrite)) {
269                                                 Assert.Fail ("Must fail");
270                                         }
271                                 } catch (IOException) {}
272                         }
273                 }
274
275                 [Test]
276                 public void MapAFileToAMemoryAreaShouldFail ()
277                 {
278                         var name = MkNamedMapping ();
279                         using (var m0 = MemoryMappedFile.CreateNew(name, 4096, MemoryMappedFileAccess.ReadWrite)) {
280                                 try {
281                                         using (var m1 = MemoryMappedFile.CreateFromFile (fname, FileMode.OpenOrCreate, name)) {
282                                                 Assert.Fail ("Must fail");
283                                         }
284                                 } catch (IOException) {}
285                         }
286                 }
287
288                 [Test]
289                 public void NamedMappingsShareMemoryArea ()
290                 {
291                         var name = MkNamedMapping ();
292                         using (var m0 = MemoryMappedFile.CreateNew(name, 4096, MemoryMappedFileAccess.ReadWrite)) {
293                                 using (var m1 = MemoryMappedFile.CreateOrOpen (name, 4096, MemoryMappedFileAccess.ReadWrite)) {
294                                         using (MemoryMappedViewAccessor v0 = m0.CreateViewAccessor (), v1 = m1.CreateViewAccessor ()) {
295                                                 v0.Write (10, 0x12345);
296                                                 Assert.AreEqual (0x12345, v1.ReadInt32 (10));
297                                         }
298                                 }
299                         }
300                 }
301
302                 [Test]
303                 public void NamedFileCanBeOpen ()
304                 {
305                         var name = MkNamedMapping ();
306                         using (var sw = new FileStream (fname, FileMode.Open)) {
307                                 byte[] b = new byte[20];
308                                 for (int i = 0; i < 20; ++i)
309                                         b[i] = 0xFF;
310                                 sw.Write (b, 0, 20);
311                         }
312
313                         using (var m0 = MemoryMappedFile.CreateFromFile (fname, FileMode.Open, name)) {
314                                 using (var m1 = MemoryMappedFile.CreateOrOpen (name, 4096)) {
315                                         using (MemoryMappedViewAccessor v0 = m0.CreateViewAccessor (), v1 = m1.CreateViewAccessor ()) {
316                                                 v0.Write (10, 0x11223344);
317                                                 Assert.AreEqual (0x11223344, v1.ReadInt32 (10));
318                                         }
319                                 }
320                         }
321                 }
322
323                 [Test]
324                 public void MapAtEdgeOfPage ()
325                 {
326                         using (var f = new FileStream (fname, FileMode.Open)) {
327                                 var b = new byte [4096];
328                                 for (int i = 0; i < 4096; ++i)
329                                         b[i] = 0xAA;
330                                 for (int i = 0; i < 2; ++i)
331                                         f.Write (b, 0, 4096);
332                         }
333                         var m0 = MemoryMappedFile.CreateFromFile (fname, FileMode.Open);
334                         var v0 = m0.CreateViewAccessor (500, 4096);
335                         var v1 = m0.CreateViewAccessor (0, 4096 * 2);
336                         for (int i = 0; i < 4096; ++i) {
337                                 Assert.AreEqual (0xAA, v1.ReadByte (i + 500));
338                                 v0.Write (i, (byte)0xFF);
339                                 Assert.AreEqual (0xFF, v1.ReadByte (i + 500));
340                         }
341                 }
342
343                 [Test]
344                 public void DoubleAccountingInOffsetCalculation ()
345                 {
346                         var memoryMappedFile90 = MemoryMappedFile.CreateNew (MkNamedMapping (), 4194304, MemoryMappedFileAccess.ReadWrite);
347                         var stream = memoryMappedFile90.CreateViewStream (4186112, 3222, MemoryMappedFileAccess.Write);
348                         using (var tw = new StreamWriter(stream))
349                         {
350                                 tw.WriteLine ("Hello World!");
351                         }
352                 }
353
354                 [Test]
355                 [ExpectedException(typeof(IOException))]
356                 public void CreateViewStreamWithOffsetPastFileEnd ()
357                 {
358                         string f = Path.Combine (tempDir, "8192-file");
359                         File.WriteAllBytes (f, new byte [8192]);
360
361                         MemoryMappedFile mappedFile = MemoryMappedFile.CreateFromFile (f, FileMode.Open, "myMap", 8192);
362
363                         /* Should throw exception when trying to map past end of file */
364                         MemoryMappedViewStream stream = mappedFile.CreateViewStream (8200, 10, MemoryMappedFileAccess.ReadWrite);
365                 }
366
367                 [Test]
368                 [ExpectedException(typeof(IOException))]
369                 public void CreateViewStreamWithOffsetPastFileEnd2 ()
370                 {
371                         string f = Path.Combine (tempDir, "8192-file");
372                         File.WriteAllBytes (f, new byte [8192]);
373
374                         MemoryMappedFile mappedFile = MemoryMappedFile.CreateFromFile (f, FileMode.Open);
375
376                         MemoryMappedViewStream stream = mappedFile.CreateViewStream (8191, 8191, MemoryMappedFileAccess.ReadWrite);
377                 }
378
379                 [Test]
380                 public void CreateViewStreamAlignToPageSize ()
381                 {
382 #if MONOTOUCH
383                         // iOS bugs on ARM64 - bnc #27667 - apple #
384                         int pageSize = (IntPtr.Size == 4) ? Environment.SystemPageSize : 4096;
385 #else
386                         int pageSize = Environment.SystemPageSize;
387 #endif
388                         string f = Path.Combine (tempDir, "p-file");
389                         File.WriteAllBytes (f, new byte [pageSize * 2 + 1]);
390
391                         MemoryMappedFile mappedFile = MemoryMappedFile.CreateFromFile (f, FileMode.Open);
392
393                         MemoryMappedViewStream stream = mappedFile.CreateViewStream (pageSize * 2, 0, MemoryMappedFileAccess.ReadWrite);
394 #if !MONOTOUCH
395                         Assert.AreEqual (stream.Capacity, Environment.SystemPageSize);
396 #endif
397                         stream.Write (new byte [pageSize], 0, pageSize);
398                 }
399
400                 [Test] // #30741 #30825
401                 public void CreateFromFileNullMapName ()
402                 {
403                         int size = 100;
404                         string f = Path.Combine (tempDir, "null-map-name-file");
405                         File.WriteAllBytes (f, new byte [size]);
406
407                         FileStream file = File.OpenRead (f);
408                         MemoryMappedFile.CreateFromFile (file, null, size, MemoryMappedFileAccess.ReadExecute, null, 0, false);
409                 }
410         }
411 }
412
413