From 36ece7a3933ec996d23d4aa0d6ae2d965e07ec8d Mon Sep 17 00:00:00 2001 From: Richard Webb Date: Thu, 8 Aug 2019 14:30:23 +0100 Subject: [PATCH] Merge PR #369: Make the custom exception types serializable * Make all the custom exception classes serializable. * Add unit tests for exception serialization. --- .../BZip2/BZip2Exception.cs | 18 +++++ .../Core/Exceptions/SharpZipBaseException.cs | 18 +++++ .../Exceptions/StreamDecodingException.cs | 18 +++++ .../Exceptions/StreamUnsupportedException.cs | 18 +++++ .../UnexpectedEndOfStreamException.cs | 18 +++++ .../Exceptions/ValueOutOfRangeException.cs | 18 +++++ .../Core/InvalidNameException.cs | 18 +++++ .../GZip/GZipException.cs | 18 +++++ .../Lzw/LzwException.cs | 18 +++++ .../Tar/TarException.cs | 18 +++++ .../Zip/ZipException.cs | 18 +++++ .../Serialization/SerializationTests.cs | 78 +++++++++++++++++++ 12 files changed, 276 insertions(+) create mode 100644 test/ICSharpCode.SharpZipLib.Tests/Serialization/SerializationTests.cs diff --git a/src/ICSharpCode.SharpZipLib/BZip2/BZip2Exception.cs b/src/ICSharpCode.SharpZipLib/BZip2/BZip2Exception.cs index a77404dde..111d21cdc 100644 --- a/src/ICSharpCode.SharpZipLib/BZip2/BZip2Exception.cs +++ b/src/ICSharpCode.SharpZipLib/BZip2/BZip2Exception.cs @@ -1,10 +1,12 @@ using System; +using System.Runtime.Serialization; namespace ICSharpCode.SharpZipLib.BZip2 { /// /// BZip2Exception represents exceptions specific to BZip2 classes and code. /// + [Serializable] public class BZip2Exception : SharpZipBaseException { /// @@ -32,5 +34,21 @@ public BZip2Exception(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the BZip2Exception class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected BZip2Exception(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/ICSharpCode.SharpZipLib/Core/Exceptions/SharpZipBaseException.cs b/src/ICSharpCode.SharpZipLib/Core/Exceptions/SharpZipBaseException.cs index 8ce046d9e..eb14e2d49 100644 --- a/src/ICSharpCode.SharpZipLib/Core/Exceptions/SharpZipBaseException.cs +++ b/src/ICSharpCode.SharpZipLib/Core/Exceptions/SharpZipBaseException.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.Serialization; namespace ICSharpCode.SharpZipLib { @@ -8,6 +9,7 @@ namespace ICSharpCode.SharpZipLib /// /// NOTE: Not all exceptions thrown will be derived from this class. /// A variety of other exceptions are possible for example + [Serializable] public class SharpZipBaseException : Exception { /// @@ -36,5 +38,21 @@ public SharpZipBaseException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the SharpZipBaseException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected SharpZipBaseException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/ICSharpCode.SharpZipLib/Core/Exceptions/StreamDecodingException.cs b/src/ICSharpCode.SharpZipLib/Core/Exceptions/StreamDecodingException.cs index df247a6bd..389b7d065 100644 --- a/src/ICSharpCode.SharpZipLib/Core/Exceptions/StreamDecodingException.cs +++ b/src/ICSharpCode.SharpZipLib/Core/Exceptions/StreamDecodingException.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.Serialization; namespace ICSharpCode.SharpZipLib { @@ -6,6 +7,7 @@ namespace ICSharpCode.SharpZipLib /// Indicates that an error occured during decoding of a input stream due to corrupt /// data or (unintentional) library incompability. /// + [Serializable] public class StreamDecodingException : SharpZipBaseException { private const string GenericMessage = "Input stream could not be decoded"; @@ -28,5 +30,21 @@ public StreamDecodingException(string message) : base(message) { } /// A message describing the exception. /// The inner exception public StreamDecodingException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the StreamDecodingException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected StreamDecodingException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/ICSharpCode.SharpZipLib/Core/Exceptions/StreamUnsupportedException.cs b/src/ICSharpCode.SharpZipLib/Core/Exceptions/StreamUnsupportedException.cs index 7fdc7a4ce..5827e559d 100644 --- a/src/ICSharpCode.SharpZipLib/Core/Exceptions/StreamUnsupportedException.cs +++ b/src/ICSharpCode.SharpZipLib/Core/Exceptions/StreamUnsupportedException.cs @@ -1,10 +1,12 @@ using System; +using System.Runtime.Serialization; namespace ICSharpCode.SharpZipLib { /// /// Indicates that the input stream could not decoded due to known library incompability or missing features /// + [Serializable] public class StreamUnsupportedException : StreamDecodingException { private const string GenericMessage = "Input stream is in a unsupported format"; @@ -27,5 +29,21 @@ public StreamUnsupportedException(string message) : base(message) { } /// A message describing the exception. /// The inner exception public StreamUnsupportedException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the StreamUnsupportedException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected StreamUnsupportedException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/ICSharpCode.SharpZipLib/Core/Exceptions/UnexpectedEndOfStreamException.cs b/src/ICSharpCode.SharpZipLib/Core/Exceptions/UnexpectedEndOfStreamException.cs index fc8391851..a35c49f03 100644 --- a/src/ICSharpCode.SharpZipLib/Core/Exceptions/UnexpectedEndOfStreamException.cs +++ b/src/ICSharpCode.SharpZipLib/Core/Exceptions/UnexpectedEndOfStreamException.cs @@ -1,10 +1,12 @@ using System; +using System.Runtime.Serialization; namespace ICSharpCode.SharpZipLib { /// /// Indicates that the input stream could not decoded due to the stream ending before enough data had been provided /// + [Serializable] public class UnexpectedEndOfStreamException : StreamDecodingException { private const string GenericMessage = "Input stream ended unexpectedly"; @@ -27,5 +29,21 @@ public UnexpectedEndOfStreamException(string message) : base(message) { } /// A message describing the exception. /// The inner exception public UnexpectedEndOfStreamException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the UnexpectedEndOfStreamException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected UnexpectedEndOfStreamException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/ICSharpCode.SharpZipLib/Core/Exceptions/ValueOutOfRangeException.cs b/src/ICSharpCode.SharpZipLib/Core/Exceptions/ValueOutOfRangeException.cs index 2af5d6e12..aefefb61e 100644 --- a/src/ICSharpCode.SharpZipLib/Core/Exceptions/ValueOutOfRangeException.cs +++ b/src/ICSharpCode.SharpZipLib/Core/Exceptions/ValueOutOfRangeException.cs @@ -1,10 +1,12 @@ using System; +using System.Runtime.Serialization; namespace ICSharpCode.SharpZipLib { /// /// Indicates that a value was outside of the expected range when decoding an input stream /// + [Serializable] public class ValueOutOfRangeException : StreamDecodingException { /// @@ -44,5 +46,21 @@ private ValueOutOfRangeException() private ValueOutOfRangeException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the ValueOutOfRangeException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected ValueOutOfRangeException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/ICSharpCode.SharpZipLib/Core/InvalidNameException.cs b/src/ICSharpCode.SharpZipLib/Core/InvalidNameException.cs index 13abfd2f2..6647631bd 100644 --- a/src/ICSharpCode.SharpZipLib/Core/InvalidNameException.cs +++ b/src/ICSharpCode.SharpZipLib/Core/InvalidNameException.cs @@ -1,10 +1,12 @@ using System; +using System.Runtime.Serialization; namespace ICSharpCode.SharpZipLib.Core { /// /// InvalidNameException is thrown for invalid names such as directory traversal paths and names with invalid characters /// + [Serializable] public class InvalidNameException : SharpZipBaseException { /// @@ -31,5 +33,21 @@ public InvalidNameException(string message) : base(message) public InvalidNameException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the InvalidNameException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected InvalidNameException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/ICSharpCode.SharpZipLib/GZip/GZipException.cs b/src/ICSharpCode.SharpZipLib/GZip/GZipException.cs index 1a5952991..a0ec6bb51 100644 --- a/src/ICSharpCode.SharpZipLib/GZip/GZipException.cs +++ b/src/ICSharpCode.SharpZipLib/GZip/GZipException.cs @@ -1,10 +1,12 @@ using System; +using System.Runtime.Serialization; namespace ICSharpCode.SharpZipLib.GZip { /// /// GZipException represents exceptions specific to GZip classes and code. /// + [Serializable] public class GZipException : SharpZipBaseException { /// @@ -32,5 +34,21 @@ public GZipException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the GZipException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected GZipException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/ICSharpCode.SharpZipLib/Lzw/LzwException.cs b/src/ICSharpCode.SharpZipLib/Lzw/LzwException.cs index 3bc0cb212..1d5c44c3c 100644 --- a/src/ICSharpCode.SharpZipLib/Lzw/LzwException.cs +++ b/src/ICSharpCode.SharpZipLib/Lzw/LzwException.cs @@ -1,10 +1,12 @@ using System; +using System.Runtime.Serialization; namespace ICSharpCode.SharpZipLib.Lzw { /// /// LzwException represents exceptions specific to LZW classes and code. /// + [Serializable] public class LzwException : SharpZipBaseException { /// @@ -32,5 +34,21 @@ public LzwException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the LzwException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected LzwException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/ICSharpCode.SharpZipLib/Tar/TarException.cs b/src/ICSharpCode.SharpZipLib/Tar/TarException.cs index c24011c79..9d448ca7d 100644 --- a/src/ICSharpCode.SharpZipLib/Tar/TarException.cs +++ b/src/ICSharpCode.SharpZipLib/Tar/TarException.cs @@ -1,10 +1,12 @@ using System; +using System.Runtime.Serialization; namespace ICSharpCode.SharpZipLib.Tar { /// /// TarException represents exceptions specific to Tar classes and code. /// + [Serializable] public class TarException : SharpZipBaseException { /// @@ -32,5 +34,21 @@ public TarException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the TarException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected TarException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/src/ICSharpCode.SharpZipLib/Zip/ZipException.cs b/src/ICSharpCode.SharpZipLib/Zip/ZipException.cs index 28843883e..ef8142b65 100644 --- a/src/ICSharpCode.SharpZipLib/Zip/ZipException.cs +++ b/src/ICSharpCode.SharpZipLib/Zip/ZipException.cs @@ -1,10 +1,12 @@ using System; +using System.Runtime.Serialization; namespace ICSharpCode.SharpZipLib.Zip { /// /// ZipException represents exceptions specific to Zip classes and code. /// + [Serializable] public class ZipException : SharpZipBaseException { /// @@ -32,5 +34,21 @@ public ZipException(string message, Exception innerException) : base(message, innerException) { } + + /// + /// Initializes a new instance of the ZipException class with serialized data. + /// + /// + /// The System.Runtime.Serialization.SerializationInfo that holds the serialized + /// object data about the exception being thrown. + /// + /// + /// The System.Runtime.Serialization.StreamingContext that contains contextual information + /// about the source or destination. + /// + protected ZipException(SerializationInfo info, StreamingContext context) + : base(info, context) + { + } } } diff --git a/test/ICSharpCode.SharpZipLib.Tests/Serialization/SerializationTests.cs b/test/ICSharpCode.SharpZipLib.Tests/Serialization/SerializationTests.cs new file mode 100644 index 000000000..6118022e4 --- /dev/null +++ b/test/ICSharpCode.SharpZipLib.Tests/Serialization/SerializationTests.cs @@ -0,0 +1,78 @@ +using System; +using System.IO; +using System.Runtime.Serialization; + +using NUnit.Framework; +using ICSharpCode.SharpZipLib.BZip2; +using ICSharpCode.SharpZipLib.Core; +using ICSharpCode.SharpZipLib.GZip; +using ICSharpCode.SharpZipLib.Lzw; +using ICSharpCode.SharpZipLib.Tar; +using ICSharpCode.SharpZipLib.Zip; + +namespace ICSharpCode.SharpZipLib.Tests.Serialization +{ + [TestFixture] + public class SerializationTests + { + /// + /// Test that SharpZipLib Custom Exceptions can be serialized. + /// + [Test] + [Category("Core")] + [Category("Serialization")] + [TestCase(typeof(BZip2Exception))] + [TestCase(typeof(GZipException))] + [TestCase(typeof(InvalidNameException))] + [TestCase(typeof(LzwException))] + [TestCase(typeof(SharpZipBaseException))] + [TestCase(typeof(StreamDecodingException))] + [TestCase(typeof(StreamUnsupportedException))] + [TestCase(typeof(TarException))] + [TestCase(typeof(UnexpectedEndOfStreamException))] + [TestCase(typeof(ZipException))] + public void SerializeException(Type exceptionType) + { + string message = $"Serialized {exceptionType.Name}"; + var exception = Activator.CreateInstance(exceptionType, message); + + var deserializedException = ExceptionSerialiseHelper(exception, exceptionType) as Exception; + Assert.That(deserializedException, Is.InstanceOf(exceptionType), "deserialized object should have the correct type"); + Assert.That(deserializedException.Message, Is.EqualTo(message), "deserialized message should match original message"); + } + + /// + /// Test that ValueOutOfRangeException can be serialized. + /// + [Test] + [Category("Core")] + [Category("Serialization")] + public void SerializeValueOutOfRangeException() + { + string message = "Serialized ValueOutOfRangeException"; + var exception = new ValueOutOfRangeException(message); + + var deserializedException = ExceptionSerialiseHelper(exception, typeof(ValueOutOfRangeException)) as ValueOutOfRangeException; + + // ValueOutOfRangeException appends 'out of range' to the end of the message + Assert.That(deserializedException.Message, Is.EqualTo($"{message} out of range"), "should have expected message"); + } + + // Shared serialization helper + // round trips the specified exception using DataContractSerializer + private static object ExceptionSerialiseHelper(object exception, Type exceptionType) + { + DataContractSerializer ser = new DataContractSerializer(exceptionType); + + using (var memoryStream = new MemoryStream()) + { + ser.WriteObject(memoryStream, exception); + + memoryStream.Seek(0, loc: SeekOrigin.Begin); + + return ser.ReadObject(memoryStream); + } + } + } +} +