123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- /*
- * 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 NUnit.Framework.SyntaxHelpers;
- 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");
- AssetBase expected = new AssetBase(uuid1, "asset one");
- 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");
- AssetBase expected = new AssetBase(UUID.Random(), "asset one");
- 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");
- AssetBase expected = new AssetBase(uuid1, "asset two");
- 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);
- }
- }
- }
|