About Jolt.NET Libraries

Inspired by the Boost C++ libraries, Jolt.NET aims to complement the .NET Base Class Library (BCL) with algorithms, data structures, and general productivity tools. It is the hope of the authors that the features of Jolt.NET will one day be part of, or represented in the BCL and the .NET Framework.

Verifying the Equality Semantics of a Type

My most recent contribution to Jolt.NET is implementing a set of assertion classes that verify if a type correctly implements equality semantics.  This is a unit testing task that is generally ignored, only because implementations of Object.Equals() are usually straightforward and implemented in terms of other types that meet this critieria.

 
When implementing an equality operator, you must make sure that it satisfies the following axioms.
 
  • Symmetry:  Equals(x,y) == Equals(y,x)
  • Transitivity:  Equals(x,z) == true if and only if (Equals(x,y) & Equals(y,z)) == true
  • Reflexivity:  Equals(x,x) == true
  • Hash-code:  Equals(x,y) => GetHashCode(x) == GetHashCode(y)
 
I've omitted the other axiom requirements for equality, which are described by the MSDN documentation for Object.Equals().
 

Jolt.NET provides the type EqualityAxiomAssertion<T> to validate the implementations of T.Equals() according to the prescribed axioms.  You may use the type as follows.

 
class TypeToValidate
{
public override bool Equals(object other)
{
return other is TypeToValidate &&
(other as TypeToValidate).m_name.Equals(m_name);
}

public string m_name;
}

class TypeFactory : IArgumentFactory<TypeToValidate>
{
public TypeFactory Create()
{
return new TypeToValidate() { m_name = "Hello world!" };
}

public void Modify(ret TypeToValdidate instance)
{
instance.m_name = "Goodbye world!";
}
}

class TestFixture()
{
void Test()
{
EqualityAxiomAssertion<TypeToValidate> assertion =
new EqualityAxiomAssertion<TypeToValidate>(new TypeFactory());

AssertionResult assertionResult = assertion.Validate();
System.Diagnostics.Debug.Assert(assertionResult.Result, assertionResult.Message);
}
}
 
IArgumentFactory<T> is required by the assertion type since it needs to be able to create and modify instances of the type that is being verified.  For instance, the transitivity axiom requires three distinct instances, and hash-code verification must modify an instance and verify that the resulting hash-code has changed.
 
There are many representations of an equality operator in .NET, some of which are Object.Equals(), IEquatable<T>, IComparable<T>, and IEqualityComparer<T>.  In addition to EqualityAxiomAssertion<T>, Jolt.NET also provides the EqualtableAxiomAssertion<T>, ComparableAxiomAssertion<T>, and EqualityComparerAxiomAssertion<T> to verify implementations of the aforementioned equality interfaces, with similar usage semantics.
 
Jolt.NET also provides a wrapper to these assertion types for straightforward integration with NUnit and Visual Studio's unit test environment.  An example of how to use a wrapped EqualityAxiomAssertion<T> in these environments follows.  Note that the remaining assertion types are also wrapped by similar methods and/or constructs.
 
class TestFixture()
{
// Visual Studio assertion model
[TestMethod]
public void EqualityAxiomTest()
{
AxiomAssert.Equality(new TypeFactory());
}
}

[TestFixture]
class NUnitFixture
{
// NUnit assertion model
[Test]
public void EqualityAxiomTest()
{
// verbose constraint model
Assert.That(typeof(TypeToValidate), new EqualityAxiomConstraint<TypeToValidate>(new TypeFactory()));

// syntax-helper constraint model
Assert.That(typeof(TypeToValidate), Implements.EqualityAxiom(new TypeFactory()));
}
}
 
The astute reader will notice that there is some duplication in the syntax for invoking the constraint.  Specifically, the type information is duplicated.  In fact, the first argument of the assertion is not used by the constraint at all -- it is merely present for readability when using a syntax helper.  Ideally, I would have liked to have used a syntax similar to the following.
 
[Test]
public void EqualityAxiomTest()
{
Assert.That<TypeToValidate>(Implements.EqualityAxiom(new TypeFactory()));
}
  
However, this is not possible for several reasons.
 
  • NUnit constraints are designed to compare an actual value against an expected value; an argument-less constraint is not possible.
    • This notiion is somewhat incompatible with the axiom assertion since the assertion is validating a property of a generic type argument.
    • The assertion could have been designed to be non-generic and operate on System.Type, but that implementation would be more cumbersome
  • An extension method on Assert is not possible since Assert methods are static.
 
Another option is to use the factory instance as the argument passed to the constraint, as in the following example.  However, while this option makes sense from an implementation standpoint, it suffers greatly in readability as a factory instance is not what is being asserted upon.
 
[Test]
public void EqualityAxiomTest()
{
// verbose constraint syntax
Assert.That(new TypeFactory(), new EqualityAxiomConstraint<TypeToValidate>());

// syntax-helper constraint syntax.
Assert.That(new TypeFactory(), Implements.EqualityAxiom<TypeToValidate>());
}
 
The documentation for these classes will be posted to the docs section shortly, and in the future, support will be added for the non-generic interfaces IEquatable, IComparable, IEqualityComparer.