Skip to content

Commit 3b4966c

Browse files
Numpsypiksel
authored andcommitted
Merge PR #302: Add a leaveOpen parameter to the ZipFile constructor
Used to set the default value of isStreamOwner. * Add a leaveOpen parameter to the ZipFile constructor, used to set the default value of isStreamOwner * Add unit tests for the leaveOpen parameter on the ZipFile constructor
1 parent 1847274 commit 3b4966c

File tree

3 files changed

+342
-4
lines changed

3 files changed

+342
-4
lines changed

src/ICSharpCode.SharpZipLib/Zip/ZipFile.cs

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -425,7 +425,25 @@ public ZipFile(string name)
425425
/// <exception cref="ZipException">
426426
/// The file doesn't contain a valid zip archive.
427427
/// </exception>
428-
public ZipFile(FileStream file)
428+
public ZipFile(FileStream file) :
429+
this(file, false)
430+
{
431+
432+
}
433+
434+
/// <summary>
435+
/// Opens a Zip file reading the given <see cref="FileStream"/>.
436+
/// </summary>
437+
/// <param name="file">The <see cref="FileStream"/> to read archive data from.</param>
438+
/// <param name="leaveOpen">true to leave the <see cref="FileStream">file</see> open when the ZipFile is disposed, false to dispose of it</param>
439+
/// <exception cref="ArgumentNullException">The supplied argument is null.</exception>
440+
/// <exception cref="IOException">
441+
/// An i/o error occurs.
442+
/// </exception>
443+
/// <exception cref="ZipException">
444+
/// The file doesn't contain a valid zip archive.
445+
/// </exception>
446+
public ZipFile(FileStream file, bool leaveOpen)
429447
{
430448
if (file == null)
431449
{
@@ -439,7 +457,7 @@ public ZipFile(FileStream file)
439457

440458
baseStream_ = file;
441459
name_ = file.Name;
442-
isStreamOwner = true;
460+
isStreamOwner = !leaveOpen;
443461

444462
try
445463
{
@@ -468,7 +486,30 @@ public ZipFile(FileStream file)
468486
/// <exception cref="ArgumentNullException">
469487
/// The <see cref="Stream">stream</see> argument is null.
470488
/// </exception>
471-
public ZipFile(Stream stream)
489+
public ZipFile(Stream stream) :
490+
this(stream, false)
491+
{
492+
493+
}
494+
495+
/// <summary>
496+
/// Opens a Zip file reading the given <see cref="Stream"/>.
497+
/// </summary>
498+
/// <param name="stream">The <see cref="Stream"/> to read archive data from.</param>
499+
/// <param name="leaveOpen">true to leave the <see cref="Stream">stream</see> open when the ZipFile is disposed, false to dispose of it</param>
500+
/// <exception cref="IOException">
501+
/// An i/o error occurs
502+
/// </exception>
503+
/// <exception cref="ZipException">
504+
/// The stream doesn't contain a valid zip archive.<br/>
505+
/// </exception>
506+
/// <exception cref="ArgumentException">
507+
/// The <see cref="Stream">stream</see> doesnt support seeking.
508+
/// </exception>
509+
/// <exception cref="ArgumentNullException">
510+
/// The <see cref="Stream">stream</see> argument is null.
511+
/// </exception>
512+
public ZipFile(Stream stream, bool leaveOpen)
472513
{
473514
if (stream == null)
474515
{
@@ -481,7 +522,7 @@ public ZipFile(Stream stream)
481522
}
482523

483524
baseStream_ = stream;
484-
isStreamOwner = true;
525+
isStreamOwner = !leaveOpen;
485526

486527
if (baseStream_.Length > 0)
487528
{

test/ICSharpCode.SharpZipLib.Tests/TestSupport/Streams.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,74 @@ public bool IsDisposed
9999
#endregion Instance Fields
100100
}
101101

102+
/// <summary>
103+
/// An extended <see cref="FileStream">file stream</see>
104+
/// that tracks closing and disposing
105+
/// </summary>
106+
public class TrackedFileStream : FileStream
107+
{
108+
/// <summary>
109+
/// Initializes a new instance of the <see cref="TrackedMemoryStream"/> class.
110+
/// </summary>
111+
/// <param name="buffer">The buffer.</param>
112+
public TrackedFileStream(string path, FileMode mode = FileMode.Open, FileAccess access = FileAccess.Read)
113+
: base(path, mode, access)
114+
{
115+
}
116+
117+
/// <summary>
118+
/// Releases the unmanaged resources used by the <see cref="T:System.IO.MemoryStream"/> class and optionally releases the managed resources.
119+
/// </summary>
120+
/// <param name="disposing">true to release both managed and unmanaged resources; false to release only unmanaged resources.</param>
121+
protected override void Dispose(bool disposing)
122+
{
123+
isDisposed_ = true;
124+
base.Dispose(disposing);
125+
}
126+
127+
/// <summary>
128+
/// Closes the current stream and releases any resources (such as sockets and file handles) associated with the current stream.
129+
/// </summary>
130+
public override void Close()
131+
{
132+
if (isClosed_)
133+
{
134+
throw new InvalidOperationException("Already closed");
135+
}
136+
137+
isClosed_ = true;
138+
base.Close();
139+
}
140+
141+
/// <summary>
142+
/// Gets a value indicating whether this instance is closed.
143+
/// </summary>
144+
/// <value><c>true</c> if this instance is closed; otherwise, <c>false</c>.</value>
145+
public bool IsClosed
146+
{
147+
get { return isClosed_; }
148+
}
149+
150+
/// <summary>
151+
/// Gets a value indicating whether this instance is disposed.
152+
/// </summary>
153+
/// <value>
154+
/// <c>true</c> if this instance is disposed; otherwise, <c>false</c>.
155+
/// </value>
156+
public bool IsDisposed
157+
{
158+
get { return isDisposed_; }
159+
}
160+
161+
#region Instance Fields
162+
163+
private bool isDisposed_;
164+
165+
private bool isClosed_;
166+
167+
#endregion Instance Fields
168+
}
169+
102170
/// <summary>
103171
/// A <see cref="Stream"/> that cannot seek.
104172
/// </summary>

test/ICSharpCode.SharpZipLib.Tests/Zip/ZipFileHandling.cs

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1138,5 +1138,234 @@ public void UnreferencedZipFileClosingPartialStream()
11381138

11391139
s.ReadByte();
11401140
}
1141+
1142+
/// <summary>
1143+
/// Check that input stream is closed when IsStreamOwner is true (default), or leaveOpen is false
1144+
/// </summary>
1145+
[Test]
1146+
[Category("Zip")]
1147+
public void StreamClosedWhenOwner()
1148+
{
1149+
var ms = new MemoryStream();
1150+
MakeZipFile(ms, false, "StreamClosedWhenOwner", 1, 10, "test");
1151+
ms.Seek(0, SeekOrigin.Begin);
1152+
var zipData = ms.ToArray();
1153+
1154+
// Stream should be closed when leaveOpen is unspecified
1155+
{
1156+
var inMemoryZip = new TrackedMemoryStream(zipData);
1157+
Assert.IsFalse(inMemoryZip.IsClosed, "Input stream should NOT be closed");
1158+
1159+
using (var zipFile = new ZipFile(inMemoryZip))
1160+
{
1161+
Assert.IsTrue(zipFile.IsStreamOwner, "Should be stream owner by default");
1162+
}
1163+
1164+
Assert.IsTrue(inMemoryZip.IsClosed, "Input stream should be closed by default");
1165+
}
1166+
1167+
// Stream should be closed when leaveOpen is false
1168+
{
1169+
var inMemoryZip = new TrackedMemoryStream(zipData);
1170+
Assert.IsFalse(inMemoryZip.IsClosed, "Input stream should NOT be closed");
1171+
1172+
using (var zipFile = new ZipFile(inMemoryZip, false))
1173+
{
1174+
Assert.IsTrue(zipFile.IsStreamOwner, "Should be stream owner when leaveOpen is false");
1175+
}
1176+
1177+
Assert.IsTrue(inMemoryZip.IsClosed, "Input stream should be closed when leaveOpen is false");
1178+
}
1179+
}
1180+
1181+
/// <summary>
1182+
/// Check that input stream is not closed when IsStreamOwner is false;
1183+
/// </summary>
1184+
[Test]
1185+
[Category("Zip")]
1186+
public void StreamNotClosedWhenNotOwner()
1187+
{
1188+
var ms = new TrackedMemoryStream();
1189+
MakeZipFile(ms, false, "StreamNotClosedWhenNotOwner", 1, 10, "test");
1190+
ms.Seek(0, SeekOrigin.Begin);
1191+
1192+
Assert.IsFalse(ms.IsClosed, "Input stream should NOT be closed");
1193+
1194+
// Stream should not be closed when leaveOpen is true
1195+
{
1196+
using (var zipFile = new ZipFile(ms, true))
1197+
{
1198+
Assert.IsFalse(zipFile.IsStreamOwner, "Should NOT be stream owner when leaveOpen is true");
1199+
}
1200+
1201+
Assert.IsFalse(ms.IsClosed, "Input stream should NOT be closed when leaveOpen is true");
1202+
}
1203+
1204+
ms.Seek(0, SeekOrigin.Begin);
1205+
1206+
// Stream should not be closed when IsStreamOwner is set to false after opening
1207+
{
1208+
using (var zipFile = new ZipFile(ms, false))
1209+
{
1210+
Assert.IsTrue(zipFile.IsStreamOwner, "Should be stream owner when leaveOpen is false");
1211+
zipFile.IsStreamOwner = false;
1212+
Assert.IsFalse(zipFile.IsStreamOwner, "Should be able to set IsStreamOwner to false");
1213+
}
1214+
1215+
Assert.IsFalse(ms.IsClosed, "Input stream should NOT be closed when IsStreamOwner is false");
1216+
}
1217+
}
1218+
1219+
/// <summary>
1220+
/// Check that input file is closed when IsStreamOwner is true (default), or leaveOpen is false
1221+
/// </summary>
1222+
[Test]
1223+
[Category("Zip")]
1224+
public void FileStreamClosedWhenOwner()
1225+
{
1226+
string tempFile = GetTempFilePath();
1227+
Assert.IsNotNull(tempFile, "No permission to execute this test?");
1228+
1229+
tempFile = Path.Combine(tempFile, "SharpZipFileStreamClosedWhenOwnerTest.Zip");
1230+
if (File.Exists(tempFile))
1231+
{
1232+
File.Delete(tempFile);
1233+
}
1234+
1235+
MakeZipFile(tempFile, "FileStreamClosedWhenOwner", 2, 10, "test");
1236+
1237+
// Stream should be closed when leaveOpen is unspecified
1238+
{
1239+
var fileStream = new TrackedFileStream(tempFile);
1240+
Assert.IsFalse(fileStream.IsClosed, "Input file should NOT be closed");
1241+
1242+
using (var zipFile = new ZipFile(fileStream))
1243+
{
1244+
Assert.IsTrue(zipFile.IsStreamOwner, "Should be stream owner by default");
1245+
}
1246+
1247+
Assert.IsTrue(fileStream.IsClosed, "Input stream should be closed by default");
1248+
}
1249+
1250+
// Stream should be closed when leaveOpen is false
1251+
{
1252+
var fileStream = new TrackedFileStream(tempFile);
1253+
Assert.IsFalse(fileStream.IsClosed, "Input stream should NOT be closed");
1254+
1255+
using (var zipFile = new ZipFile(fileStream, false))
1256+
{
1257+
Assert.IsTrue(zipFile.IsStreamOwner, "Should be stream owner when leaveOpen is false");
1258+
}
1259+
1260+
Assert.IsTrue(fileStream.IsClosed, "Input stream should be closed when leaveOpen is false");
1261+
}
1262+
1263+
File.Delete(tempFile);
1264+
}
1265+
1266+
/// <summary>
1267+
/// Check that input file is not closed when IsStreamOwner is false;
1268+
/// </summary>
1269+
[Test]
1270+
[Category("Zip")]
1271+
public void FileStreamNotClosedWhenNotOwner()
1272+
{
1273+
string tempFile = GetTempFilePath();
1274+
Assert.IsNotNull(tempFile, "No permission to execute this test?");
1275+
1276+
tempFile = Path.Combine(tempFile, "SharpZipFileStreamNotClosedWhenNotOwner.Zip");
1277+
if (File.Exists(tempFile))
1278+
{
1279+
File.Delete(tempFile);
1280+
}
1281+
1282+
MakeZipFile(tempFile, "FileStreamClosedWhenOwner", 2, 10, "test");
1283+
1284+
// Stream should not be closed when leaveOpen is true
1285+
{
1286+
using (var fileStream = new TrackedFileStream(tempFile))
1287+
{
1288+
Assert.IsFalse(fileStream.IsClosed, "Input file should NOT be closed");
1289+
1290+
using (var zipFile = new ZipFile(fileStream, true))
1291+
{
1292+
Assert.IsFalse(zipFile.IsStreamOwner, "Should NOT be stream owner when leaveOpen is true");
1293+
}
1294+
1295+
Assert.IsFalse(fileStream.IsClosed, "Input stream should NOT be closed when leaveOpen is true");
1296+
}
1297+
}
1298+
1299+
// Stream should not be closed when IsStreamOwner is set to false after opening
1300+
{
1301+
using (var fileStream = new TrackedFileStream(tempFile))
1302+
{
1303+
Assert.IsFalse(fileStream.IsClosed, "Input file should NOT be closed");
1304+
1305+
using (var zipFile = new ZipFile(fileStream, false))
1306+
{
1307+
Assert.IsTrue(zipFile.IsStreamOwner, "Should be stream owner when leaveOpen is false");
1308+
zipFile.IsStreamOwner = false;
1309+
Assert.IsFalse(zipFile.IsStreamOwner, "Should be able to set IsStreamOwner to false");
1310+
}
1311+
1312+
Assert.IsFalse(fileStream.IsClosed, "Input stream should NOT be closed when leaveOpen is true");
1313+
}
1314+
}
1315+
1316+
File.Delete(tempFile);
1317+
}
1318+
1319+
/// <summary>
1320+
/// Check that input stream is closed when construction fails and leaveOpen is false
1321+
/// </summary>
1322+
[Test]
1323+
public void StreamClosedOnError()
1324+
{
1325+
var ms = new TrackedMemoryStream(new byte[32]);
1326+
1327+
Assert.IsFalse(ms.IsClosed, "Underlying stream should NOT be closed initially");
1328+
bool blewUp = false;
1329+
try
1330+
{
1331+
using (var zipFile = new ZipFile(ms, false))
1332+
{
1333+
Assert.Fail("Exception not thrown");
1334+
}
1335+
}
1336+
catch
1337+
{
1338+
blewUp = true;
1339+
}
1340+
1341+
Assert.IsTrue(blewUp, "Should have failed to load the file");
1342+
Assert.IsTrue(ms.IsClosed, "Underlying stream should be closed");
1343+
}
1344+
1345+
/// <summary>
1346+
/// Check that input stream is not closed when construction fails and leaveOpen is true
1347+
/// </summary>
1348+
[Test]
1349+
public void StreamNotClosedOnError()
1350+
{
1351+
var ms = new TrackedMemoryStream(new byte[32]);
1352+
1353+
Assert.IsFalse(ms.IsClosed, "Underlying stream should NOT be closed initially");
1354+
bool blewUp = false;
1355+
try
1356+
{
1357+
using (var zipFile = new ZipFile(ms, true))
1358+
{
1359+
Assert.Fail("Exception not thrown");
1360+
}
1361+
}
1362+
catch
1363+
{
1364+
blewUp = true;
1365+
}
1366+
1367+
Assert.IsTrue(blewUp, "Should have failed to load the file");
1368+
Assert.IsFalse(ms.IsClosed, "Underlying stream should NOT be closed");
1369+
}
11411370
}
11421371
}

0 commit comments

Comments
 (0)