OpenSim
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Events Macros
PropertyCompareConstraint.cs
Go to the documentation of this file.
1 /*
2  * Copyright (c) Contributors, http://opensimulator.org/
3  * See CONTRIBUTORS.TXT for a full list of copyright holders.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  * * Redistributions of source code must retain the above copyright
8  * notice, this list of conditions and the following disclaimer.
9  * * Redistributions in binary form must reproduce the above copyright
10  * notice, this list of conditions and the following disclaimer in the
11  * documentation and/or other materials provided with the distribution.
12  * * Neither the name of the OpenSimulator Project nor the
13  * names of its contributors may be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19  * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 using System;
29 using System.Collections;
30 using System.Collections.Generic;
31 using System.Drawing;
32 using System.Linq;
33 using System.Linq.Expressions;
34 using System.Reflection;
35 using NUnit.Framework;
36 using NUnit.Framework.Constraints;
37 using OpenMetaverse;
38 using OpenSim.Framework;
39 using OpenSim.Tests.Common;
40 
41 namespace OpenSim.Data.Tests
42 {
43  public static class Constraints
44  {
45  //This is here because C# has a gap in the language, you can't infer type from a constructor
46  public static PropertyCompareConstraint<T> PropertyCompareConstraint<T>(T expected)
47  {
48  return new PropertyCompareConstraint<T>(expected);
49  }
50  }
51 
52  public class PropertyCompareConstraint<T> : NUnit.Framework.Constraints.Constraint
53  {
54  private readonly object _expected;
55  //the reason everywhere uses propertyNames.Reverse().ToArray() is because the stack is backwards of the order we want to display the properties in.
56  private string failingPropertyName = string.Empty;
57  private object failingExpected;
58  private object failingActual;
59 
60  public PropertyCompareConstraint(T expected)
61  {
62  _expected = expected;
63  }
64 
65  public override bool Matches(object actual)
66  {
67  return ObjectCompare(_expected, actual, new Stack<string>());
68  }
69 
70  private bool ObjectCompare(object expected, object actual, Stack<string> propertyNames)
71  {
72  //If they are both null, they are equal
73  if (actual == null && expected == null)
74  return true;
75 
76  //If only one is null, then they aren't
77  if (actual == null || expected == null)
78  {
79  failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
80  failingActual = actual;
81  failingExpected = expected;
82  return false;
83  }
84 
85  //prevent loops...
86  if (propertyNames.Count > 50)
87  {
88  failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
89  failingActual = actual;
90  failingExpected = expected;
91  return false;
92  }
93 
94  if (actual.GetType() != expected.GetType())
95  {
96  propertyNames.Push("GetType()");
97  failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
98  propertyNames.Pop();
99  failingActual = actual.GetType();
100  failingExpected = expected.GetType();
101  return false;
102  }
103 
104  if (actual.GetType() == typeof(Color))
105  {
106  Color actualColor = (Color) actual;
107  Color expectedColor = (Color) expected;
108  if (actualColor.R != expectedColor.R)
109  {
110  propertyNames.Push("R");
111  failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
112  propertyNames.Pop();
113  failingActual = actualColor.R;
114  failingExpected = expectedColor.R;
115  return false;
116  }
117  if (actualColor.G != expectedColor.G)
118  {
119  propertyNames.Push("G");
120  failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
121  propertyNames.Pop();
122  failingActual = actualColor.G;
123  failingExpected = expectedColor.G;
124  return false;
125  }
126  if (actualColor.B != expectedColor.B)
127  {
128  propertyNames.Push("B");
129  failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
130  propertyNames.Pop();
131  failingActual = actualColor.B;
132  failingExpected = expectedColor.B;
133  return false;
134  }
135  if (actualColor.A != expectedColor.A)
136  {
137  propertyNames.Push("A");
138  failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
139  propertyNames.Pop();
140  failingActual = actualColor.A;
141  failingExpected = expectedColor.A;
142  return false;
143  }
144  return true;
145  }
146 
147  IComparable comp = actual as IComparable;
148  if (comp != null)
149  {
150  if (comp.CompareTo(expected) != 0)
151  {
152  failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
153  failingActual = actual;
154  failingExpected = expected;
155  return false;
156  }
157  return true;
158  }
159 
160  //Now try the much more annoying IComparable<T>
161  Type icomparableInterface = actual.GetType().GetInterface("IComparable`1");
162  if (icomparableInterface != null)
163  {
164  int result = (int)icomparableInterface.GetMethod("CompareTo").Invoke(actual, new[] { expected });
165  if (result != 0)
166  {
167  failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
168  failingActual = actual;
169  failingExpected = expected;
170  return false;
171  }
172  return true;
173  }
174 
175  IEnumerable arr = actual as IEnumerable;
176  if (arr != null)
177  {
178  List<object> actualList = arr.Cast<object>().ToList();
179  List<object> expectedList = ((IEnumerable)expected).Cast<object>().ToList();
180  if (actualList.Count != expectedList.Count)
181  {
182  propertyNames.Push("Count");
183  failingPropertyName = string.Join(".", propertyNames.Reverse().ToArray());
184  failingActual = actualList.Count;
185  failingExpected = expectedList.Count;
186  propertyNames.Pop();
187  return false;
188  }
189  //actualList and expectedList should be the same size.
190  for (int i = 0; i < actualList.Count; i++)
191  {
192  propertyNames.Push("[" + i + "]");
193  if (!ObjectCompare(expectedList[i], actualList[i], propertyNames))
194  return false;
195  propertyNames.Pop();
196  }
197  //Everything seems okay...
198  return true;
199  }
200 
201  //Skip static properties. I had a nasty problem comparing colors because of all of the public static colors.
202  PropertyInfo[] properties = expected.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance);
203  foreach (var property in properties)
204  {
205  if (ignores.Contains(property.Name))
206  continue;
207 
208  object actualValue = property.GetValue(actual, null);
209  object expectedValue = property.GetValue(expected, null);
210 
211  propertyNames.Push(property.Name);
212  if (!ObjectCompare(expectedValue, actualValue, propertyNames))
213  return false;
214  propertyNames.Pop();
215  }
216 
217  return true;
218  }
219 
220  public override void WriteDescriptionTo(MessageWriter writer)
221  {
222  writer.WriteExpectedValue(failingExpected);
223  }
224 
225  public override void WriteActualValueTo(MessageWriter writer)
226  {
227  writer.WriteActualValue(failingActual);
228  writer.WriteLine();
229  writer.Write(" On Property: " + failingPropertyName);
230  }
231 
232  //These notes assume the lambda: (x=>x.Parent.Value)
233  //ignores should really contain like a fully dotted version of the property name, but I'm starting with small steps
234  readonly List<string> ignores = new List<string>();
235  public PropertyCompareConstraint<T> IgnoreProperty(Expression<Func<T, object>> func)
236  {
237  Expression express = func.Body;
238  PullApartExpression(express);
239 
240  return this;
241  }
242 
243  private void PullApartExpression(Expression express)
244  {
245  //This deals with any casts... like implicit casts to object. Not all UnaryExpression are casts, but this is a first attempt.
246  if (express is UnaryExpression)
247  PullApartExpression(((UnaryExpression)express).Operand);
248  if (express is MemberExpression)
249  {
250  //If the inside of the lambda is the access to x, we've hit the end of the chain.
251  // We should track by the fully scoped parameter name, but this is the first rev of doing this.
252  ignores.Add(((MemberExpression)express).Member.Name);
253  }
254  }
255  }
256 
257  [TestFixture]
259  {
260  public class HasInt
261  {
262  public int TheValue { get; set; }
263  }
264 
265  [Test]
266  public void IntShouldMatch()
267  {
268  HasInt actual = new HasInt { TheValue = 5 };
269  HasInt expected = new HasInt { TheValue = 5 };
270  var constraint = Constraints.PropertyCompareConstraint(expected);
271 
272  Assert.That(constraint.Matches(actual), Is.True);
273  }
274 
275  [Test]
276  public void IntShouldNotMatch()
277  {
278  HasInt actual = new HasInt { TheValue = 5 };
279  HasInt expected = new HasInt { TheValue = 4 };
280  var constraint = Constraints.PropertyCompareConstraint(expected);
281 
282  Assert.That(constraint.Matches(actual), Is.False);
283  }
284 
285 
286  [Test]
287  public void IntShouldIgnore()
288  {
289  HasInt actual = new HasInt { TheValue = 5 };
290  HasInt expected = new HasInt { TheValue = 4 };
291  var constraint = Constraints.PropertyCompareConstraint(expected).IgnoreProperty(x => x.TheValue);
292 
293  Assert.That(constraint.Matches(actual), Is.True);
294  }
295 
296  [Test]
297  public void AssetShouldMatch()
298  {
299  UUID uuid1 = UUID.Random();
300  AssetBase actual = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString());
301  AssetBase expected = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString());
302 
303  var constraint = Constraints.PropertyCompareConstraint(expected);
304 
305  Assert.That(constraint.Matches(actual), Is.True);
306  }
307 
308  [Test]
309  public void AssetShouldNotMatch()
310  {
311  UUID uuid1 = UUID.Random();
312  AssetBase actual = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString());
313  AssetBase expected = new AssetBase(UUID.Random(), "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString());
314 
315  var constraint = Constraints.PropertyCompareConstraint(expected);
316 
317  Assert.That(constraint.Matches(actual), Is.False);
318  }
319 
320  [Test]
321  public void AssetShouldNotMatch2()
322  {
323  UUID uuid1 = UUID.Random();
324  AssetBase actual = new AssetBase(uuid1, "asset one", (sbyte)AssetType.Texture, UUID.Zero.ToString());
325  AssetBase expected = new AssetBase(uuid1, "asset two", (sbyte)AssetType.Texture, UUID.Zero.ToString());
326 
327  var constraint = Constraints.PropertyCompareConstraint(expected);
328 
329  Assert.That(constraint.Matches(actual), Is.False);
330  }
331 
332  [Test]
333  public void UUIDShouldMatch()
334  {
335  UUID uuid1 = UUID.Random();
336  UUID uuid2 = UUID.Parse(uuid1.ToString());
337 
338  var constraint = Constraints.PropertyCompareConstraint(uuid1);
339 
340  Assert.That(constraint.Matches(uuid2), Is.True);
341  }
342 
343  [Test]
344  public void UUIDShouldNotMatch()
345  {
346  UUID uuid1 = UUID.Random();
347  UUID uuid2 = UUID.Random();
348 
349  var constraint = Constraints.PropertyCompareConstraint(uuid1);
350 
351  Assert.That(constraint.Matches(uuid2), Is.False);
352  }
353 
354  [Test]
355  public void TestColors()
356  {
357  Color actual = Color.Red;
358  Color expected = Color.FromArgb(actual.A, actual.R, actual.G, actual.B);
359 
360  var constraint = Constraints.PropertyCompareConstraint(expected);
361 
362  Assert.That(constraint.Matches(actual), Is.True);
363  }
364 
365  [Test]
366  public void ShouldCompareLists()
367  {
368  List<int> expected = new List<int> { 1, 2, 3 };
369  List<int> actual = new List<int> { 1, 2, 3 };
370 
371  var constraint = Constraints.PropertyCompareConstraint(expected);
372  Assert.That(constraint.Matches(actual), Is.True);
373  }
374 
375 
376  [Test]
378  {
379  List<int> expected = new List<int> { 1, 2, 3 };
380  List<int> actual = new List<int> { 1, 2, 4 };
381 
382  var constraint = Constraints.PropertyCompareConstraint(expected);
383  Assert.That(constraint.Matches(actual), Is.False);
384  }
385 
386  [Test]
388  {
389  List<int> expected = new List<int> { 1, 2, 3 };
390  List<int> actual = new List<int> { 1, 2 };
391 
392  var constraint = Constraints.PropertyCompareConstraint(expected);
393  Assert.That(constraint.Matches(actual), Is.False);
394  }
395 
396  public class Recursive
397  {
398  public Recursive Other { get; set; }
399  }
400 
401  [Test]
402  public void ErrorsOutOnRecursive()
403  {
404  Recursive parent = new Recursive();
405  Recursive child = new Recursive();
406  parent.Other = child;
407  child.Other = parent;
408 
409  var constraint = Constraints.PropertyCompareConstraint(child);
410  Assert.That(constraint.Matches(child), Is.False);
411  }
412  }
413 }
PropertyCompareConstraint< T > IgnoreProperty(Expression< Func< T, object >> func)
Asset class. All Assets are reference by this class or a class derived from this class ...
Definition: AssetBase.cs:49
System.Collections.IEnumerable IEnumerable