Skip to content

Commit

Permalink
Add ExceptionTests.
Browse files Browse the repository at this point in the history
  • Loading branch information
sharpjs committed Apr 18, 2022
1 parent fe3d464 commit e0c426f
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Most lines should begin with one of these words:
-->

## [2.0.0](https://github.com/sharpjs/Subatomix.Testing/compare/release/1.1.5..release/2.0.0)
- Add `ExceptionTests`.
- Add support for .NET 6.0
- `BREAKING` Remove support for .NET Core 2.1
- Update Coverlet to [3.1.2](https://github.com/coverlet-coverage/coverlet/blob/master/Documentation/Changelog.md#release-date-2022-02-06)
Expand Down
24 changes: 24 additions & 0 deletions Subatomix.Testing.Tests/ExceptionTestsTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
Copyright 2022 Jeffrey Sharp
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

namespace Subatomix.Testing;

[TestFixture]
[Parallelizable(ParallelScope.All)]
public class ExceptionTestsTests : ExceptionTests<Exception>
{
// Tests defined by base class
}
163 changes: 163 additions & 0 deletions Subatomix.Testing/ExceptionTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
/*
Copyright 2022 Jeffrey Sharp
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/

using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;

using static System.Reflection.BindingFlags;

namespace Subatomix.Testing;

// Squelch Visual Studio's incorrect hint to remove the next suppression
#pragma warning disable IDE0079

// Allow obsolete BinaryFormatter to test legacy deserialization constructor
// https://docs.microsoft.com/en-us/dotnet/fundamentals/syslib-diagnostics/syslib0011
#pragma warning disable SYSLIB0011

/// <summary>
/// Base class for tests of exception types.
/// </summary>
/// <typeparam name="T">
/// The exception type to test.
/// </typeparam>
public abstract class ExceptionTests<T>
where T : Exception
{
// As told by Dennis Ritchie:
// https://www.bell-labs.com/usr/dmr/www/odd.html
private const string ArcaneMessage = "values of β will give rise to dom!";

[Test]
public virtual void Construct_Default()
{
var exception = Create();

exception.Message .Should().NotBeNullOrWhiteSpace();
exception.InnerException.Should().BeNull();
}

[Test]
public virtual void Construct_Message()
{
var exception = Create(ArcaneMessage);

exception.Message .Should().BeSameAs(ArcaneMessage);
exception.InnerException.Should().BeNull();
}

[Test]
public virtual void Construct_Message_Null()
{
var exception = Create(null as string);

exception.Message .Should().NotBeNullOrWhiteSpace();
exception.InnerException.Should().BeNull();
}

[Test]
public virtual void Construct_MessageAndInnerException()
{
var innerException = new InvalidProgramException();
var exception = Create(ArcaneMessage, innerException);

exception.Message .Should().BeSameAs(ArcaneMessage);
exception.InnerException.Should().BeSameAs(innerException);
}

[Test]
public virtual void Construct_MessageAndInnerException_NullMessage()
{
var innerException = new InvalidProgramException();
var exception = Create(null as string, innerException);

exception.Message .Should().NotBeNullOrWhiteSpace();
exception.InnerException.Should().BeSameAs(innerException);
}

[Test]
public virtual void Construct_MessageAndInnerException_NullInnerException()
{
var exception = Create(ArcaneMessage, null as Exception);

exception.Message .Should().BeSameAs(ArcaneMessage);
exception.InnerException.Should().BeNull();
}

[Test]
public void SerializableAttribute()
{
typeof(T).IsSerializable.Should().BeTrue();
}

[Test]
public void DeserializationConstructor()
{
var constructor = typeof(T).GetConstructor(
Instance | Public | NonPublic | ExactBinding,
null,
new[] { typeof(SerializationInfo), typeof(StreamingContext) },
null
);

constructor.Should().NotBeNull(
"exception type {0} must provide a deserialization constructor; " +
"see notes in the test for further information",
typeof(T).FullName
);

constructor!.IsFamily.Should().BeTrue(
"exception type {0} deserialization constructor must be protected; " +
"see notes in the test for further information",
typeof(T).FullName
);
}

[Test]
public virtual void SerializeThenDeserialize() // tests protected serialization constructor
{
var innerException = new InvalidProgramException();
var exception = Create(ArcaneMessage, innerException);

var deserialized = Roundtrip(exception);

deserialized .Should().NotBeNull();
deserialized.Message .Should().Be(ArcaneMessage);
deserialized.InnerException.Should().BeOfType<InvalidProgramException>();
}

private static T Create(params object?[] args)
{
// T is Exception or some type derived from it, not Nullable<_>.
// Thus Activator.CreateInstance is guaranteed to not return null.
return (T) Activator.CreateInstance(typeof(T), args)!;
}

private static T Roundtrip(T obj)
{
using var memory = new MemoryStream();

// Serialize
var formatter = new BinaryFormatter();
formatter.Serialize(memory, obj);

// Rewind
memory.Position = 0;

// Deserialize
return (T) formatter.Deserialize(memory);
}
}

0 comments on commit e0c426f

Please sign in to comment.