Files
opensim/OpenSim/Data/Tests/PropertyCompareConstraint.cs
Justin Clark-Casey (justincc) 9456bb77fb Upgrade nunit.framework.dll to version 2.5.9. Fix up tests appropriately.
This version removes the NUnit.Framework.SyntaxHelpers namespace, so any modules with their own tests will need to delete this using statement.
2011-03-09 23:25:24 +00:00

412 lines
16 KiB
C#

/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using NUnit.Framework;
using NUnit.Framework.Constraints;
using OpenMetaverse;
using OpenSim.Framework;
namespace OpenSim.Data.Tests
{
public static class Constraints
{
//This is here because C# has a gap in the language, you can't infer type from a constructor
public static PropertyCompareConstraint<T> PropertyCompareConstraint<T>(T expected)
{
return new PropertyCompareConstraint<T>(expected);
}
}
public class PropertyCompareConstraint<T> : NUnit.Framework.Constraints.Constraint
{
private readonly object _expected;
//the reason everywhere uses propertyNames.Reverse().ToArray() is because the stack is backwards of the order we want to display the properties in.
private string failingPropertyName = string.Empty;
private object failingExpected;
private object failingActual;
public PropertyCompareConstraint(T expected)
{
_expected = expected;
}
public override bool Matches(object actual)
{
return ObjectCompare(_expected, actual, new Stack<string>());
}
private bool ObjectCompare(object expected, object actual, Stack<string> propertyNames)
{
//If they are both null, they are equal
if (actual == null && expected == null)
return true;
//If only one is null, then they aren't
if (actual == null || expected == null)
{
failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
failingActual = actual;
failingExpected = expected;
return false;
}
//prevent loops...
if (propertyNames.Count > 50)
{
failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
failingActual = actual;
failingExpected = expected;
return false;
}
if (actual.GetType() != expected.GetType())
{
propertyNames.Push("GetType()");
failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
propertyNames.Pop();
failingActual = actual.GetType();
failingExpected = expected.GetType();
return false;
}
if (actual.GetType() == typeof(Color))
{
Color actualColor = (Color) actual;
Color expectedColor = (Color) expected;
if (actualColor.R != expectedColor.R)
{
propertyNames.Push("R");
failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
propertyNames.Pop();
failingActual = actualColor.R;
failingExpected = expectedColor.R;
return false;
}
if (actualColor.G != expectedColor.G)
{
propertyNames.Push("G");
failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
propertyNames.Pop();
failingActual = actualColor.G;
failingExpected = expectedColor.G;
return false;
}
if (actualColor.B != expectedColor.B)
{
propertyNames.Push("B");
failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
propertyNames.Pop();
failingActual = actualColor.B;
failingExpected = expectedColor.B;
return false;
}
if (actualColor.A != expectedColor.A)
{
propertyNames.Push("A");
failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
propertyNames.Pop();
failingActual = actualColor.A;
failingExpected = expectedColor.A;
return false;
}
return true;
}
IComparable comp = actual as IComparable;
if (comp != null)
{
if (comp.CompareTo(expected) != 0)
{
failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
failingActual = actual;
failingExpected = expected;
return false;
}
return true;
}
//Now try the much more annoying IComparable<T>
Type icomparableInterface = actual.GetType().GetInterface("IComparable`1");
if (icomparableInterface != null)
{
int result = (int)icomparableInterface.GetMethod("CompareTo").Invoke(actual, new[] { expected });
if (result != 0)
{
failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
failingActual = actual;
failingExpected = expected;
return false;
}
return true;
}
IEnumerable arr = actual as IEnumerable;
if (arr != null)
{
List<object> actualList = arr.Cast<object>().ToList();
List<object> expectedList = ((IEnumerable)expected).Cast<object>().ToList();
if (actualList.Count != expectedList.Count)
{
propertyNames.Push("Count");
failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
failingActual = actualList.Count;
failingExpected = expectedList.Count;
propertyNames.Pop();
return false;
}
//actualList and expectedList should be the same size.
for (int i = 0; i < actualList.Count; i++)
{
propertyNames.Push("[" + i + "]");
if (!ObjectCompare(expectedList[i], actualList[i], propertyNames))
return false;
propertyNames.Pop();
}
//Everything seems okay...
return true;
}
//Skip static properties. I had a nasty problem comparing colors because of all of the public static colors.
PropertyInfo[] properties = expected.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
if (ignores.Contains(property.Name))
continue;
object actualValue = property.GetValue(actual, null);
object expectedValue = property.GetValue(expected, null);
propertyNames.Push(property.Name);
if (!ObjectCompare(expectedValue, actualValue, propertyNames))
return false;
propertyNames.Pop();
}
return true;
}
public override void WriteDescriptionTo(MessageWriter writer)
{
writer.WriteExpectedValue(failingExpected);
}
public override void WriteActualValueTo(MessageWriter writer)
{
writer.WriteActualValue(failingActual);
writer.WriteLine();
writer.Write(" On Property: " + failingPropertyName);
}
//These notes assume the lambda: (x=>x.Parent.Value)
//ignores should really contain like a fully dotted version of the property name, but I'm starting with small steps
readonly List<string> ignores = new List<string>();
public PropertyCompareConstraint<T> IgnoreProperty(Expression<Func<T, object>> func)
{
Expression express = func.Body;
PullApartExpression(express);
return this;
}
private void PullApartExpression(Expression express)
{
//This deals with any casts... like implicit casts to object. Not all UnaryExpression are casts, but this is a first attempt.
if (express is UnaryExpression)
PullApartExpression(((UnaryExpression)express).Operand);
if (express is MemberExpression)
{
//If the inside of the lambda is the access to x, we've hit the end of the chain.
// We should track by the fully scoped parameter name, but this is the first rev of doing this.
ignores.Add(((MemberExpression)express).Member.Name);
}
}
}
[TestFixture]
public class PropertyCompareConstraintTest
{
public class HasInt
{
public int TheValue { get; set; }
}
[Test]
public void IntShouldMatch()
{
HasInt actual = new HasInt { TheValue = 5 };
HasInt expected = new HasInt { TheValue = 5 };
var constraint = Constraints.PropertyCompareConstraint(expected);
Assert.That(constraint.Matches(actual), Is.True);
}
[Test]
public void IntShouldNotMatch()
{
HasInt actual = new HasInt { TheValue = 5 };
HasInt expected = new HasInt { TheValue = 4 };
var constraint = Constraints.PropertyCompareConstraint(expected);
Assert.That(constraint.Matches(actual), Is.False);
}
[Test]
public void IntShouldIgnore()
{
HasInt actual = new HasInt { TheValue = 5 };
HasInt expected = new HasInt { TheValue = 4 };
var constraint = Constraints.PropertyCompareConstraint(expected).IgnoreProperty(x => x.TheValue);
Assert.That(constraint.Matches(actual), Is.True);
}
[Test]
public void AssetShouldMatch()
{
UUID uuid1 = UUID.Random();
AssetBase actual = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString());
AssetBase expected = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString());
var constraint = Constraints.PropertyCompareConstraint(expected);
Assert.That(constraint.Matches(actual), Is.True);
}
[Test]
public void AssetShouldNotMatch()
{
UUID uuid1 = UUID.Random();
AssetBase actual = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString());
AssetBase expected = new AssetBase(UUID.Random(), "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString());
var constraint = Constraints.PropertyCompareConstraint(expected);
Assert.That(constraint.Matches(actual), Is.False);
}
[Test]
public void AssetShouldNotMatch2()
{
UUID uuid1 = UUID.Random();
AssetBase actual = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString());
AssetBase expected = new AssetBase(uuid1, "asset two", (sbyte)AssetType.Texture, UUID.Zero.ToString());
var constraint = Constraints.PropertyCompareConstraint(expected);
Assert.That(constraint.Matches(actual), Is.False);
}
[Test]
public void UUIDShouldMatch()
{
UUID uuid1 = UUID.Random();
UUID uuid2 = UUID.Parse(uuid1.ToString());
var constraint = Constraints.PropertyCompareConstraint(uuid1);
Assert.That(constraint.Matches(uuid2), Is.True);
}
[Test]
public void UUIDShouldNotMatch()
{
UUID uuid1 = UUID.Random();
UUID uuid2 = UUID.Random();
var constraint = Constraints.PropertyCompareConstraint(uuid1);
Assert.That(constraint.Matches(uuid2), Is.False);
}
[Test]
public void TestColors()
{
Color actual = Color.Red;
Color expected = Color.FromArgb(actual.A, actual.R, actual.G, actual.B);
var constraint = Constraints.PropertyCompareConstraint(expected);
Assert.That(constraint.Matches(actual), Is.True);
}
[Test]
public void ShouldCompareLists()
{
List<int> expected = new List<int> { 1, 2, 3 };
List<int> actual = new List<int> { 1, 2, 3 };
var constraint = Constraints.PropertyCompareConstraint(expected);
Assert.That(constraint.Matches(actual), Is.True);
}
[Test]
public void ShouldFailToCompareListsThatAreDifferent()
{
List<int> expected = new List<int> { 1, 2, 3 };
List<int> actual = new List<int> { 1, 2, 4 };
var constraint = Constraints.PropertyCompareConstraint(expected);
Assert.That(constraint.Matches(actual), Is.False);
}
[Test]
public void ShouldFailToCompareListsThatAreDifferentLengths()
{
List<int> expected = new List<int> { 1, 2, 3 };
List<int> actual = new List<int> { 1, 2 };
var constraint = Constraints.PropertyCompareConstraint(expected);
Assert.That(constraint.Matches(actual), Is.False);
}
public class Recursive
{
public Recursive Other { get; set; }
}
[Test]
public void ErrorsOutOnRecursive()
{
Recursive parent = new Recursive();
Recursive child = new Recursive();
parent.Other = child;
child.Other = parent;
var constraint = Constraints.PropertyCompareConstraint(child);
Assert.That(constraint.Matches(child), Is.False);
}
}
}