using Unity.SharpZipLib.GZip;
using Unity.SharpZipLib.Tests.TestSupport;
using NUnit.Framework;
using System;
using System.IO;
using System.Text;
namespace Unity.SharpZipLib.Tests.GZip
{
///
/// This class contains test cases for GZip compression
///
[TestFixture]
internal class GZipTestSuite
{
///
/// Basic compress/decompress test
///
[Test]
[Category("GZip")]
public void TestGZip()
{
var ms = new MemoryStream();
var outStream = new GZipOutputStream(ms);
byte[] buf = new byte[100000];
var rnd = new Random();
rnd.NextBytes(buf);
outStream.Write(buf, 0, buf.Length);
outStream.Flush();
outStream.Finish();
ms.Seek(0, SeekOrigin.Begin);
var inStream = new GZipInputStream(ms);
byte[] buf2 = new byte[buf.Length];
int currentIndex = 0;
int count = buf2.Length;
while (true)
{
int numRead = inStream.Read(buf2, currentIndex, count);
if (numRead <= 0)
{
break;
}
currentIndex += numRead;
count -= numRead;
}
Assert.AreEqual(0, count);
for (int i = 0; i < buf.Length; ++i)
{
Assert.AreEqual(buf2[i], buf[i]);
}
}
///
/// Writing GZip headers is delayed so that this stream can be used with HTTP/IIS.
///
[Test]
[Category("GZip")]
public void DelayedHeaderWriteNoData()
{
var ms = new MemoryStream();
Assert.AreEqual(0, ms.Length);
using (GZipOutputStream outStream = new GZipOutputStream(ms))
{
Assert.AreEqual(0, ms.Length);
}
byte[] data = ms.ToArray();
Assert.IsTrue(data.Length > 0);
}
///
/// Variant of DelayedHeaderWriteNoData testing flushing for https://github.com/icsharpcode/SharpZipLib/issues/382
///
[Test]
[Category("GZip")]
public void DelayedHeaderWriteFlushNoData()
{
var ms = new MemoryStream();
Assert.AreEqual(0, ms.Length);
using (GZipOutputStream outStream = new GZipOutputStream(ms) { IsStreamOwner = false })
{
// #382 - test flushing the stream before writing to it.
outStream.Flush();
}
ms.Seek(0, SeekOrigin.Begin);
// Test that the gzip stream can be read
var readStream = new MemoryStream();
using (GZipInputStream inStream = new GZipInputStream(ms))
{
inStream.CopyTo(readStream);
}
byte[] data = readStream.ToArray();
Assert.That(data, Is.Empty, "Should not have any decompressed data");
}
///
/// Writing GZip headers is delayed so that this stream can be used with HTTP/IIS.
///
[Test]
[Category("GZip")]
public void DelayedHeaderWriteWithData()
{
var ms = new MemoryStream();
Assert.AreEqual(0, ms.Length);
using (GZipOutputStream outStream = new GZipOutputStream(ms))
{
Assert.AreEqual(0, ms.Length);
outStream.WriteByte(45);
// Should in fact contain header right now with
// 1 byte in the compression pipeline
Assert.AreEqual(10, ms.Length);
}
byte[] data = ms.ToArray();
Assert.IsTrue(data.Length > 0);
}
///
/// variant of DelayedHeaderWriteWithData to test https://github.com/icsharpcode/SharpZipLib/issues/382
///
[Test]
[Category("GZip")]
public void DelayedHeaderWriteFlushWithData()
{
var ms = new MemoryStream();
Assert.AreEqual(0, ms.Length);
using (GZipOutputStream outStream = new GZipOutputStream(ms) { IsStreamOwner = false })
{
Assert.AreEqual(0, ms.Length);
// #382 - test flushing the stream before writing to it.
outStream.Flush();
outStream.WriteByte(45);
}
ms.Seek(0, SeekOrigin.Begin);
// Test that the gzip stream can be read
var readStream = new MemoryStream();
using (GZipInputStream inStream = new GZipInputStream(ms))
{
inStream.CopyTo(readStream);
}
// Check that the data was read
byte[] data = readStream.ToArray();
CollectionAssert.AreEqual(new byte[] { 45 }, data, "Decompressed data should match initial data");
}
[Test]
[Category("GZip")]
public void ZeroLengthInputStream()
{
var gzi = new GZipInputStream(new MemoryStream());
bool exception = false;
int retval = int.MinValue;
try
{
retval = gzi.ReadByte();
}
catch
{
exception = true;
}
Assert.IsFalse(exception, "reading from an empty stream should not cause an exception");
Assert.That(retval, Is.EqualTo(-1), "should yield -1 byte value");
}
[Test]
[Category("GZip")]
public void OutputStreamOwnership()
{
var memStream = new TrackedMemoryStream();
var s = new GZipOutputStream(memStream);
Assert.IsFalse(memStream.IsClosed, "Shouldnt be closed initially");
Assert.IsFalse(memStream.IsDisposed, "Shouldnt be disposed initially");
s.Close();
Assert.IsTrue(memStream.IsClosed, "Should be closed after parent owner close");
Assert.IsTrue(memStream.IsDisposed, "Should be disposed after parent owner close");
memStream = new TrackedMemoryStream();
s = new GZipOutputStream(memStream);
Assert.IsFalse(memStream.IsClosed, "Shouldnt be closed initially");
Assert.IsFalse(memStream.IsDisposed, "Shouldnt be disposed initially");
s.IsStreamOwner = false;
s.Close();
Assert.IsFalse(memStream.IsClosed, "Should not be closed after parent owner close");
Assert.IsFalse(memStream.IsDisposed, "Should not be disposed after parent owner close");
}
[Test]
[Category("GZip")]
public void InputStreamOwnership()
{
var memStream = new TrackedMemoryStream();
var s = new GZipInputStream(memStream);
Assert.IsFalse(memStream.IsClosed, "Shouldnt be closed initially");
Assert.IsFalse(memStream.IsDisposed, "Shouldnt be disposed initially");
s.Close();
Assert.IsTrue(memStream.IsClosed, "Should be closed after parent owner close");
Assert.IsTrue(memStream.IsDisposed, "Should be disposed after parent owner close");
memStream = new TrackedMemoryStream();
s = new GZipInputStream(memStream);
Assert.IsFalse(memStream.IsClosed, "Shouldnt be closed initially");
Assert.IsFalse(memStream.IsDisposed, "Shouldnt be disposed initially");
s.IsStreamOwner = false;
s.Close();
Assert.IsFalse(memStream.IsClosed, "Should not be closed after parent owner close");
Assert.IsFalse(memStream.IsDisposed, "Should not be disposed after parent owner close");
}
[Test]
public void DoubleFooter()
{
var memStream = new TrackedMemoryStream();
var s = new GZipOutputStream(memStream);
s.Finish();
Int64 length = memStream.Length;
s.Close();
Assert.AreEqual(length, memStream.ToArray().Length);
}
[Test]
public void DoubleClose()
{
var memStream = new TrackedMemoryStream();
var s = new GZipOutputStream(memStream);
s.Finish();
s.Close();
s.Close();
memStream = new TrackedMemoryStream();
using (GZipOutputStream no2 = new GZipOutputStream(memStream))
{
s.Close();
}
}
[Test]
public void WriteAfterFinish()
{
var memStream = new TrackedMemoryStream();
var s = new GZipOutputStream(memStream);
s.Finish();
try
{
s.WriteByte(7);
Assert.Fail("Write should fail");
}
catch
{
}
}
[Test]
public void WriteAfterClose()
{
var memStream = new TrackedMemoryStream();
var s = new GZipOutputStream(memStream);
s.Close();
try
{
s.WriteByte(7);
Assert.Fail("Write should fail");
}
catch
{
}
}
///
/// Verify that if a decompression was successful for at least one block we're exiting gracefully.
///
[Test]
public void TrailingGarbage()
{
/* ARRANGE */
var ms = new MemoryStream();
var outStream = new GZipOutputStream(ms);
// input buffer to be compressed
byte[] buf = new byte[100000];
var rnd = new Random();
rnd.NextBytes(buf);
// compress input buffer
outStream.Write(buf, 0, buf.Length);
outStream.Flush();
outStream.Finish();
// generate random trailing garbage and add to the compressed stream
byte[] garbage = new byte[4096];
rnd.NextBytes(garbage);
ms.Write(garbage, 0, garbage.Length);
// rewind the concatenated stream
ms.Seek(0, SeekOrigin.Begin);
/* ACT */
// decompress concatenated stream
var inStream = new GZipInputStream(ms);
byte[] buf2 = new byte[buf.Length];
int currentIndex = 0;
int count = buf2.Length;
while (true)
{
int numRead = inStream.Read(buf2, currentIndex, count);
if (numRead <= 0)
{
break;
}
currentIndex += numRead;
count -= numRead;
}
/* ASSERT */
Assert.AreEqual(0, count);
for (int i = 0; i < buf.Length; ++i)
{
Assert.AreEqual(buf2[i], buf[i]);
}
}
///
/// Test that if we flush a GZip output stream then all data that has been written
/// is flushed through to the underlying stream and can be successfully read back
/// even if the stream is not yet finished.
///
[Test]
[Category("GZip")]
public void FlushToUnderlyingStream()
{
var ms = new MemoryStream();
var outStream = new GZipOutputStream(ms);
byte[] buf = new byte[100000];
var rnd = new Random();
rnd.NextBytes(buf);
outStream.Write(buf, 0, buf.Length);
// Flush output stream but don't finish it yet
outStream.Flush();
ms.Seek(0, SeekOrigin.Begin);
var inStream = new GZipInputStream(ms);
byte[] buf2 = new byte[buf.Length];
int currentIndex = 0;
int count = buf2.Length;
while (true)
{
try
{
int numRead = inStream.Read(buf2, currentIndex, count);
if (numRead <= 0)
{
break;
}
currentIndex += numRead;
count -= numRead;
}
catch (GZipException)
{
// We should get an unexpected EOF exception once we've read all
// data as the stream isn't yet finished.
break;
}
}
Assert.AreEqual(0, count);
for (int i = 0; i < buf.Length; ++i)
{
Assert.AreEqual(buf2[i], buf[i]);
}
}
[Test]
[Category("GZip")]
public void SmallBufferDecompression()
{
var outputBufferSize = 100000;
var inputBufferSize = outputBufferSize * 4;
var outputBuffer = new byte[outputBufferSize];
var inputBuffer = new byte[inputBufferSize];
using (var msGzip = new MemoryStream())
{
using (var gzos = new GZipOutputStream(msGzip))
{
gzos.IsStreamOwner = false;
var rnd = new Random(0);
rnd.NextBytes(inputBuffer);
gzos.Write(inputBuffer, 0, inputBuffer.Length);
gzos.Flush();
gzos.Finish();
}
msGzip.Seek(0, SeekOrigin.Begin);
using (var gzis = new GZipInputStream(msGzip))
using (var msRaw = new MemoryStream())
{
int readOut;
while ((readOut = gzis.Read(outputBuffer, 0, outputBuffer.Length)) > 0)
{
msRaw.Write(outputBuffer, 0, readOut);
}
var resultBuffer = msRaw.ToArray();
for (var i = 0; i < resultBuffer.Length; i++)
{
Assert.AreEqual(inputBuffer[i], resultBuffer[i]);
}
}
}
}
///
/// Should gracefully handle reading from a stream that becomes unreadable after
/// all of the data has been read.
///
///
/// Test for https://github.com/icsharpcode/SharpZipLib/issues/379
///
[Test]
[Category("Zip")]
public void ShouldGracefullyHandleReadingANonReableStream()
{
MemoryStream ms = new SelfClosingStream();
using (var gzos = new GZipOutputStream(ms))
{
gzos.IsStreamOwner = false;
byte[] buf = new byte[100000];
var rnd = new Random();
rnd.NextBytes(buf);
gzos.Write(buf, 0, buf.Length);
}
ms.Seek(0, SeekOrigin.Begin);
using (var gzis = new GZipInputStream(ms))
using (var msRaw = new MemoryStream())
{
gzis.CopyTo(msRaw);
}
}
[Test]
[Category("GZip")]
[Category("Performance")]
[Category("Long Running")]
[Explicit("Long Running")]
public void WriteThroughput()
{
PerformanceTesting.TestWrite(
size: TestDataSize.Large,
output: w => new GZipOutputStream(w)
);
}
[Test]
[Category("GZip")]
[Category("Performance")]
[Explicit("Long Running")]
public void ReadWriteThroughput()
{
PerformanceTesting.TestReadWrite(
size: TestDataSize.Large,
input: w => new GZipInputStream(w),
output: w => new GZipOutputStream(w)
);
}
///
/// Basic compress/decompress test
///
[Test]
[Category("GZip")]
public void OriginalFilename()
{
var content = "FileContents";
using (var ms = new MemoryStream())
{
using (var outStream = new GZipOutputStream(ms) { IsStreamOwner = false })
{
outStream.FileName = "/path/to/file.ext";
var writeBuffer = Encoding.ASCII.GetBytes(content);
outStream.Write(writeBuffer, 0, writeBuffer.Length);
outStream.Flush();
outStream.Finish();
}
ms.Seek(0, SeekOrigin.Begin);
using (var inStream = new GZipInputStream(ms))
{
var readBuffer = new byte[content.Length];
inStream.Read(readBuffer, 0, readBuffer.Length);
Assert.AreEqual(content, Encoding.ASCII.GetString(readBuffer));
Assert.AreEqual("file.ext", inStream.GetFilename());
}
}
}
}
}