1 // ***********************************************************************
2 // Copyright (c) 2009 Charlie Poole
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 // ***********************************************************************
25 #define NUNIT_FRAMEWORK
30 using System.Collections;
31 using System.Collections.Generic;
32 using NUnit.Compatibility;
33 using System.Reflection;
35 namespace NUnit.Framework.Constraints
39 /// EqualityAdapter class handles all equality comparisons
40 /// that use an <see cref="IEqualityComparer"/>, <see cref="IEqualityComparer{T}"/>
41 /// or a <see cref="ComparisonAdapter"/>.
43 public abstract class EqualityAdapter
46 /// Compares two objects, returning true if they are equal
48 public abstract bool AreEqual(object x, object y);
51 /// Returns true if the two objects can be compared by this adapter.
52 /// The base adapter cannot handle IEnumerables except for strings.
54 public virtual bool CanCompare(object x, object y)
56 if (x is string && y is string)
59 if (x is IEnumerable || y is IEnumerable)
65 #region Nested IComparer Adapter
68 /// Returns an <see cref="EqualityAdapter"/> that wraps an <see cref="IComparer"/>.
70 public static EqualityAdapter For(IComparer comparer)
72 return new ComparerAdapter(comparer);
76 /// <see cref="EqualityAdapter"/> that wraps an <see cref="IComparer"/>.
78 class ComparerAdapter : EqualityAdapter
80 private IComparer comparer;
82 public ComparerAdapter(IComparer comparer)
84 this.comparer = comparer;
87 public override bool AreEqual(object x, object y)
89 return comparer.Compare(x, y) == 0;
95 #region Nested IEqualityComparer Adapter
98 /// Returns an <see cref="EqualityAdapter"/> that wraps an <see cref="IEqualityComparer"/>.
100 public static EqualityAdapter For(IEqualityComparer comparer)
102 return new EqualityComparerAdapter(comparer);
105 class EqualityComparerAdapter : EqualityAdapter
107 private IEqualityComparer comparer;
109 public EqualityComparerAdapter(IEqualityComparer comparer)
111 this.comparer = comparer;
114 public override bool AreEqual(object x, object y)
116 return comparer.Equals(x, y);
121 /// Returns an EqualityAdapter that uses a predicate function for items comparison.
123 /// <typeparam name="TExpected"></typeparam>
124 /// <typeparam name="TActual"></typeparam>
125 /// <param name="comparison"></param>
126 /// <returns></returns>
127 public static EqualityAdapter For<TExpected, TActual>(Func<TExpected, TActual, bool> comparison)
129 return new PredicateEqualityAdapter<TExpected, TActual>(comparison);
132 internal class PredicateEqualityAdapter<TActual, TExpected> : EqualityAdapter
134 private readonly Func<TActual, TExpected, bool> _comparison;
137 /// Returns true if the two objects can be compared by this adapter.
138 /// The base adapter cannot handle IEnumerables except for strings.
140 public override bool CanCompare(object x, object y)
146 /// Compares two objects, returning true if they are equal
148 public override bool AreEqual(object x, object y)
150 return _comparison.Invoke((TActual)y, (TExpected)x);
153 public PredicateEqualityAdapter(Func<TActual, TExpected, bool> comparison)
155 _comparison = comparison;
161 #region Nested GenericEqualityAdapter<T>
163 abstract class GenericEqualityAdapter<T> : EqualityAdapter
166 /// Returns true if the two objects can be compared by this adapter.
167 /// Generic adapter requires objects of the specified type.
169 public override bool CanCompare(object x, object y)
171 return typeof(T).GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo())
172 && typeof(T).GetTypeInfo().IsAssignableFrom(y.GetType().GetTypeInfo());
175 protected void ThrowIfNotCompatible(object x, object y)
177 if (!typeof(T).GetTypeInfo().IsAssignableFrom(x.GetType().GetTypeInfo()))
178 throw new ArgumentException("Cannot compare " + x.ToString());
180 if (!typeof(T).GetTypeInfo().IsAssignableFrom(y.GetType().GetTypeInfo()))
181 throw new ArgumentException("Cannot compare " + y.ToString());
187 #region Nested IEqualityComparer<T> Adapter
190 /// Returns an <see cref="EqualityAdapter"/> that wraps an <see cref="IEqualityComparer{T}"/>.
192 public static EqualityAdapter For<T>(IEqualityComparer<T> comparer)
194 return new EqualityComparerAdapter<T>(comparer);
197 class EqualityComparerAdapter<T> : GenericEqualityAdapter<T>
199 private IEqualityComparer<T> comparer;
201 public EqualityComparerAdapter(IEqualityComparer<T> comparer)
203 this.comparer = comparer;
206 public override bool AreEqual(object x, object y)
208 ThrowIfNotCompatible(x, y);
209 return comparer.Equals((T)x, (T)y);
215 #region Nested IComparer<T> Adapter
218 /// Returns an <see cref="EqualityAdapter"/> that wraps an <see cref="IComparer{T}"/>.
220 public static EqualityAdapter For<T>(IComparer<T> comparer)
222 return new ComparerAdapter<T>(comparer);
226 /// <see cref="EqualityAdapter"/> that wraps an <see cref="IComparer"/>.
228 class ComparerAdapter<T> : GenericEqualityAdapter<T>
230 private IComparer<T> comparer;
232 public ComparerAdapter(IComparer<T> comparer)
234 this.comparer = comparer;
237 public override bool AreEqual(object x, object y)
239 ThrowIfNotCompatible(x, y);
240 return comparer.Compare((T)x, (T)y) == 0;
246 #region Nested Comparison<T> Adapter
249 /// Returns an <see cref="EqualityAdapter"/> that wraps a <see cref="Comparison{T}"/>.
251 public static EqualityAdapter For<T>(Comparison<T> comparer)
253 return new ComparisonAdapter<T>(comparer);
256 class ComparisonAdapter<T> : GenericEqualityAdapter<T>
258 private Comparison<T> comparer;
260 public ComparisonAdapter(Comparison<T> comparer)
262 this.comparer = comparer;
265 public override bool AreEqual(object x, object y)
267 ThrowIfNotCompatible(x, y);
268 return comparer.Invoke((T)x, (T)y) == 0;