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