Merge pull request #4118 from petertiedemann/fix-bug-48516
[mono.git] / mcs / class / System.IO.Compression / Test / System.IO.Compression / ZipTest.cs
1 //
2 // ZipTests.cs
3 //
4 // Author:
5 //         Joao Matos <joao.matos@xamarin.com>
6 //
7 // Copyright (c) 2013 Xamarin Inc. (http://www.xamarin.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining a copy
10 // of this software and associated documentation files (the "Software"), to deal
11 // in the Software without restriction, including without limitation the rights
12 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13 // copies of the Software, and to permit persons to whom the Software is
14 // furnished to do so, subject to the following conditions:
15 //
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
18 //
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 // THE SOFTWARE.
26
27 using System;
28 using System.IO;
29 using System.IO.Compression;
30 using System.Linq;
31 using System.Security.Cryptography;
32 using NUnit.Framework;
33
34 namespace MonoTests.System.IO.Compression
35 {
36         [TestFixture]
37         public class ZipArchiveTests
38         {
39                 static string GetSHA1HashFromFile(Stream stream)
40                 {
41                         using (var sha1 = SHA1.Create())
42                         {
43                                 return BitConverter.ToString(sha1.ComputeHash(stream))
44                                         .Replace("-", string.Empty);
45                         }
46                 }
47
48                 [Test]
49                 public void ZipGetEntryReadMode()
50                 {
51                         File.Copy("archive.zip", "test.zip", overwrite: true);
52                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
53                                 ZipArchiveMode.Read))
54                         {
55                                 var entry = archive.GetEntry("foo.txt");
56                                 Assert.IsNotNull(entry);
57
58                                 var nullEntry = archive.GetEntry("nonexisting");
59                                 Assert.IsNull(nullEntry);
60                         }
61
62                         File.Delete ("test.zip");
63                 }
64
65                 [Test]
66                 public void ZipGetEntryCreateMode()
67                 {
68                         File.Copy("archive.zip", "test.zip", overwrite: true);
69                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
70                                 ZipArchiveMode.Create))
71                         {
72                                 try {
73                                         archive.GetEntry("foo");
74                                 } catch(NotSupportedException ex) {
75                                         return;
76                                 }
77
78                                 Assert.Fail();
79                         }
80
81                         File.Delete ("test.zip");
82                 }
83
84                 [Test]
85                 public void ZipGetEntryUpdateMode()
86                 {
87                         File.Copy("archive.zip", "test.zip", overwrite: true);
88                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
89                                 ZipArchiveMode.Read))
90                         {
91                                 var entry = archive.GetEntry("foo.txt");
92                                 Assert.IsNotNull(entry);
93
94                                 var nullEntry = archive.GetEntry("nonexisting");
95                                 Assert.IsNull(nullEntry);
96                         }
97
98                         File.Delete ("test.zip");
99                 }
100
101                 [Test]
102                 public void ZipGetEntryOpen()
103                 {
104                         File.Copy("archive.zip", "test.zip", overwrite: true);
105                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
106                                 ZipArchiveMode.Read))
107                         {
108                                 var entry = archive.GetEntry("foo.txt");
109                                 Assert.IsNotNull(entry);
110
111                                 var foo = entry.Open();
112                         }
113
114                         File.Delete ("test.zip");
115                 }
116
117                 [Test]
118                 public void ZipOpenAndReopenEntry()
119                 {
120                         try {
121                                 File.Copy("archive.zip", "test.zip", overwrite: true);
122                                 using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
123                                         ZipArchiveMode.Update))
124                                 {
125                                         var entry = archive.GetEntry("foo.txt");
126                                         Assert.IsNotNull(entry);
127
128                                         var stream = entry.Open();
129
130                                         try {
131                                                 stream = entry.Open();
132                                         } catch (global::System.IO.IOException ex) {
133                                                 return;
134                                         }
135
136                                         Assert.Fail();
137                                 }
138                         } finally {
139                                 File.Delete ("test.zip");
140                         }
141                 }
142
143
144                 [Test]
145                 public void ZipOpenCloseAndReopenEntry()
146                 {
147                         File.Copy("archive.zip", "test.zip", overwrite: true);
148                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
149                                 ZipArchiveMode.Update))
150                         {
151                                 var entry = archive.GetEntry("foo.txt");
152                                 Assert.IsNotNull(entry);
153
154                                 var stream = entry.Open();
155                                 stream.Dispose();
156                                 stream = entry.Open();
157                         }
158
159                         File.Delete ("test.zip");
160                 }
161
162                 [Test]
163                 public void ZipGetEntryDeleteReadMode()
164                 {
165                         File.Copy("archive.zip", "delete.zip", overwrite: true);
166                         using (var archive = new ZipArchive(File.Open("delete.zip", FileMode.Open),
167                                 ZipArchiveMode.Update))
168                         {
169                                 var entry = archive.GetEntry("foo.txt");
170                                 Assert.IsNotNull(entry);
171
172                                 entry.Delete();
173                         }
174
175                         using (var archive = new ZipArchive(File.Open("delete.zip", FileMode.Open),
176                                 ZipArchiveMode.Read))
177                         {
178                                 var entry = archive.GetEntry("foo.txt");
179                                 Assert.IsNull(entry);
180                         }
181
182                         File.Delete ("delete.zip");
183                 }
184
185                 [Test]
186                 public void ZipDeleteEntryCheckEntries()
187                 {
188                         File.Copy("archive.zip", "delete.zip", overwrite: true);
189                         using (var archive = new ZipArchive(File.Open("delete.zip", FileMode.Open),
190                                 ZipArchiveMode.Update))
191                         {
192                                 var entry = archive.GetEntry("foo.txt");
193                                 Assert.IsNotNull(entry);
194
195                                 entry.Delete();
196
197                                 Assert.IsNull(archive.Entries.FirstOrDefault(e => e == entry));
198                         }
199
200                         File.Delete ("delete.zip");
201                 }               
202
203                 [Test]
204                 public void ZipGetEntryDeleteUpdateMode()
205                 {
206                         File.Copy("archive.zip", "delete.zip", overwrite: true);
207                         using (var archive = new ZipArchive(File.Open("delete.zip", FileMode.Open),
208                                 ZipArchiveMode.Update))
209                         {
210                                 var entry = archive.GetEntry("foo.txt");
211                                 Assert.IsNotNull(entry);
212
213                                 entry.Delete();
214                         }
215
216                         using (var archive = new ZipArchive(File.Open("delete.zip", FileMode.Open),
217                                 ZipArchiveMode.Read))
218                         {
219                                 var entry = archive.GetEntry("foo.txt");
220                                 Assert.IsNull(entry);
221                         }
222
223                         File.Delete ("delete.zip");
224                 }
225
226                 [Test]
227                 public void ZipCreateArchive()
228                 {
229                         using (var archive = new ZipArchive(File.Open("create.zip", FileMode.Create),
230                                 ZipArchiveMode.Create))
231                         {
232                                 var dir = archive.CreateEntry("foobar/");
233
234                                 var entry = archive.CreateEntry("foo.txt");
235                                 using (var stream = entry.Open())
236                                 {
237                                         using (var streamWriter = new StreamWriter(stream))
238                                                 streamWriter.Write("foo");
239                                 }
240                         }
241
242                         using (var archive = new ZipArchive(File.Open("create.zip", FileMode.Open),
243                                 ZipArchiveMode.Read))
244                         {
245                                 Assert.IsNotNull(archive.GetEntry("foobar/"));
246
247                                 var entry = archive.GetEntry("foo.txt");
248                                 Assert.IsNotNull(entry);
249
250                                 var streamReader = new StreamReader(entry.Open());
251                                 var text = streamReader.ReadToEnd();
252
253                                 Assert.AreEqual("foo", text);
254                         }
255
256                         File.Delete ("create.zip");
257                 }
258
259                 [Test]
260                 public void ZipEnumerateEntriesModifiedTime()
261                 {
262                         File.Copy("archive.zip", "test.zip", overwrite: true);
263                         var date = DateTimeOffset.Now;
264                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
265                                 ZipArchiveMode.Update))
266                         {
267                                 var entry = archive.GetEntry("foo.txt");
268                                 entry.LastWriteTime = date;
269                         }
270
271                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
272                                 ZipArchiveMode.Read))
273                         {
274                                 var entry = archive.GetEntry("foo.txt");
275                                 Assert.AreEqual(entry.LastWriteTime.Year, date.Year);
276                                 Assert.AreEqual(entry.LastWriteTime.Month, date.Month);
277                                 Assert.AreEqual(entry.LastWriteTime.Day, date.Day);
278
279                         }
280
281                         File.Delete ("test.zip");
282                 }               
283
284                 [Test]
285                 public void ZipEnumerateArchiveDefaultLastWriteTime()
286                 {
287                         using (var archive = new ZipArchive(File.Open("test.nupkg", FileMode.Open),
288                                 ZipArchiveMode.Read))
289                         {
290                                 var entry = archive.GetEntry("_rels/.rels");
291                                 Assert.AreEqual(new DateTime(624511296000000000).Ticks, entry.LastWriteTime.Ticks);
292                                 Assert.IsNotNull(entry);
293                         }
294                 }
295
296                 public void ZipGetArchiveEntryStreamLengthPosition(ZipArchiveMode mode)
297                 {
298                         File.Copy("test.nupkg", "test2.nupkg", overwrite: true);
299                         using (var archive = new ZipArchive(File.Open("test2.nupkg", FileMode.Open), mode))
300                         {
301                                 var entry = archive.GetEntry("_rels/.rels");
302                                 using (var stream = entry.Open())
303                                 {
304                                         Assert.AreEqual(0, stream.Position);
305                                         Assert.AreEqual(425, stream.Length);
306                                 }
307
308                                 // .NET does not support these in Read mode but we do.
309                                 var entry2 = archive.GetEntry("modernhttpclient.nuspec");
310                                 using (var stream = entry2.Open())
311                                 {
312                                         Assert.AreEqual(857, stream.Length);
313                                         if (mode == ZipArchiveMode.Update)
314                                         {
315                                                 Assert.AreEqual(0, stream.Position);
316                                         }
317                                 }
318                         }
319                         File.Delete ("test2.nupkg");    
320                 }
321
322                 [Test]
323                 public void ZipGetArchiveEntryStreamLengthPositionReadMode()
324                 {
325                         ZipGetArchiveEntryStreamLengthPosition(ZipArchiveMode.Read);
326                 }
327
328                 [Test]
329                 public void ZipGetArchiveEntryStreamLengthPositionUpdateMode()
330                 {
331                         ZipGetArchiveEntryStreamLengthPosition(ZipArchiveMode.Update);
332                 }               
333
334                 [Test]
335                 public void ZipEnumerateEntriesReadMode()
336                 {
337                         File.Copy("archive.zip", "test.zip", overwrite: true);
338                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
339                                 ZipArchiveMode.Read))
340                         {
341                                 var entries = archive.Entries;
342                                 Assert.AreEqual(5, entries.Count);
343
344                                 Assert.AreEqual("bar.txt", entries[0].FullName);
345                                 Assert.AreEqual("foo.txt", entries[1].FullName);
346                                 Assert.AreEqual("foobar/", entries[2].FullName);
347                                 Assert.AreEqual("foobar/bar.txt", entries[3].FullName);
348                                 Assert.AreEqual("foobar/foo.txt", entries[4].FullName);
349                         }
350
351                         File.Delete ("test.zip");
352                 }
353
354                 [Test]
355                 public void ZipWriteEntriesUpdateMode()
356                 {
357                         File.Copy("archive.zip", "test.zip", overwrite: true);
358                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
359                                 ZipArchiveMode.Update))
360                         {
361                                 var foo = archive.GetEntry("foo.txt");
362                                 using (var stream = foo.Open())
363                                 using (var sw = new StreamWriter(stream))
364                                 {
365                                         sw.Write("TEST");
366                                 }
367                         }
368
369                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
370                                 ZipArchiveMode.Read))
371                         {
372                                 var foo = archive.GetEntry("foo.txt");
373                                 using (var stream = foo.Open())
374                                 using (var sr = new StreamReader(stream))
375                                 {
376                                         var line = sr.ReadLine();
377                                         Assert.AreEqual("TEST", line);
378                                 }
379                         }
380
381                         File.Delete ("test.zip");
382                 }
383
384                 [Test]
385                 public void ZipWriteEntriesUpdateModeNewEntry()
386                 {
387                         var stream = new MemoryStream();
388                         var zipArchive = new ZipArchive(stream, ZipArchiveMode.Update);
389
390                         var newEntry = zipArchive.CreateEntry("testEntry");
391
392                         using (var newStream = newEntry.Open())
393                         {
394                                 using (var sw = new StreamWriter(newStream))
395                                 {
396                                         sw.Write("TEST");
397                                 }
398                         }
399                 }
400
401                 [Test]
402                 public void ZipCreateDuplicateEntriesUpdateMode()
403                 {
404                         var stream = new MemoryStream();
405                         using (var zipArchive = new ZipArchive(stream, ZipArchiveMode.Update, true))
406                         {
407                                 var e2 = zipArchive.CreateEntry("BBB");
408                                 var e3 = zipArchive.CreateEntry("BBB");
409                         }
410
411                         stream.Position = 0;
412                         using (var zipArchive = new ZipArchive(stream, ZipArchiveMode.Read))
413                         {
414                                 Assert.AreEqual(2, zipArchive.Entries.Count);
415                         }
416                 }
417
418                 [Test]
419                 public void ZipWriteEntriesUpdateModeNonZeroPosition()
420                 {
421                         File.Copy("archive.zip", "test.zip", overwrite: true);
422                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
423                                 ZipArchiveMode.Update))
424                         {
425                                 var foo = archive.GetEntry("foo.txt");
426                                 using (var stream = foo.Open())
427                                 {
428                                         var line = stream.ReadByte();
429                                         using (var sw = new StreamWriter(stream))
430                                         {
431                                                 sw.Write("TEST");
432                                         }
433                                 }
434                         }
435
436                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
437                                 ZipArchiveMode.Read))
438                         {
439                                 var entries = archive.Entries;
440                                 var foo = archive.GetEntry("foo.txt");
441                                 using (var stream = foo.Open())
442                                 using (var sr = new StreamReader(stream))
443                                 {
444                                         var line = sr.ReadLine();
445                                         Assert.AreEqual("fTEST", line);
446                                 }
447                         }
448
449                         File.Delete ("test.zip");
450                 }
451
452                 [Test]
453                 public void ZipEnumerateEntriesUpdateMode()
454                 {
455                         File.Copy("archive.zip", "test.zip", overwrite: true);
456                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
457                                 ZipArchiveMode.Read))
458                         {
459                                 var entries = archive.Entries;
460                                 Assert.AreEqual(5, entries.Count);
461
462                                 Assert.AreEqual("bar.txt", entries[0].FullName);
463                                 Assert.AreEqual("foo.txt", entries[1].FullName);
464                                 Assert.AreEqual("foobar/", entries[2].FullName);
465                                 Assert.AreEqual("foobar/bar.txt", entries[3].FullName);
466                                 Assert.AreEqual("foobar/foo.txt", entries[4].FullName);
467                         }
468
469                         File.Delete ("test.zip");
470                 }
471
472                 [Test]
473                 public void ZipEnumerateEntriesCreateMode()
474                 {
475                         File.Copy("archive.zip", "test.zip", overwrite: true);
476                         using (var archive = new ZipArchive(File.Open("test.zip", FileMode.Open),
477                                 ZipArchiveMode.Create))
478                         {
479                                 try {
480                                         archive.Entries.ToList();
481                                 } catch(NotSupportedException ex) {
482                                         return;
483                                 }
484                                 
485                                 Assert.Fail();                          
486                         }
487
488                         File.Delete ("test.zip");
489                 }
490
491                 [Test]
492                 public void ZipUpdateEmptyArchive()
493                 {
494                         File.WriteAllText("empty.zip", string.Empty);
495                         using (var archive = new ZipArchive(File.Open("empty.zip", FileMode.Open),
496                                 ZipArchiveMode.Update))
497                         {
498                         }
499                         File.Delete ("empty.zip");
500                 }
501
502                 class MyFakeStream : FileStream 
503                 {
504                         public MyFakeStream (string path, FileMode mode) : base(path, mode) {}
505
506                         /// <summary>
507                         /// Simulate "CanSeek" is false, which is the case when you are retreiving data from web.
508                         /// </summary>
509                         public override bool CanSeek => false;
510
511                         public override long Position {
512                                 get {throw new NotSupportedException();}
513                                 set {throw new NotSupportedException();}
514                         }
515                 }
516
517                 [Test]
518                 public void ZipReadNonSeekableStream()
519                 {
520                         var stream = new MyFakeStream("test.nupkg", FileMode.Open);
521                         using (var archive = new ZipArchive (stream, ZipArchiveMode.Read))
522                         {
523                         }
524                 }
525
526                 [Test]
527                 public void ZipWriteNonSeekableStream() {
528                         var stream = new MyFakeStream( "test.nupkg", FileMode.Open );
529                         using ( var archive = new ZipArchive( stream, ZipArchiveMode.Create ) ) {
530                                 var entry = archive.CreateEntry( "foo" );
531                                 using ( var es = entry.Open() ) {
532                                         es.Write( new byte[] { 4, 2 }, 0, 2 );
533                                 }
534                         }
535                 }
536         }
537 }