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