[NUI] Rebase develnui (DevelNUI only patches --> master) (#3910)
[platform/core/csapi/tizenfx.git] / test / Tizen.NUI.Devel.Tests.Ubuntu / nunit.framework / Constraints / NUnitEqualityComparer.cs
1 // ***********************************************************************
2 // Copyright (c) 2009 Charlie Poole
3 //
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:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
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 // ***********************************************************************
23 #define PORTABLE
24 #define TIZEN
25 #define NUNIT_FRAMEWORK
26 #define NUNITLITE
27 #define NET_4_5
28 #define PARALLEL
29 using System;
30 using System.IO;
31 using System.Collections;
32 using System.Collections.Generic;
33 using System.Reflection;
34 using NUnit.Compatibility;
35
36 namespace NUnit.Framework.Constraints
37 {
38     /// <summary>
39     /// NUnitEqualityComparer encapsulates NUnit's handling of
40     /// equality tests between objects.
41     /// </summary>
42     public class NUnitEqualityComparer
43     {
44         #region Static and Instance Fields
45         /// <summary>
46         /// If true, all string comparisons will ignore case
47         /// </summary>
48         private bool caseInsensitive;
49
50         /// <summary>
51         /// If true, arrays will be treated as collections, allowing
52         /// those of different dimensions to be compared
53         /// </summary>
54         private bool compareAsCollection;
55
56         /// <summary>
57         /// Comparison objects used in comparisons for some constraints.
58         /// </summary>
59         private List<EqualityAdapter> externalComparers = new List<EqualityAdapter>();
60
61         /// <summary>
62         /// List of points at which a failure occurred.
63         /// </summary>
64         private List<FailurePoint> failurePoints;
65
66         private static readonly int BUFFER_SIZE = 4096;
67         #endregion
68
69         #region Properties
70
71         /// <summary>
72         /// Returns the default NUnitEqualityComparer
73         /// </summary>
74         public static NUnitEqualityComparer Default
75         {
76             get { return new NUnitEqualityComparer(); }
77         }
78         /// <summary>
79         /// Gets and sets a flag indicating whether case should
80         /// be ignored in determining equality.
81         /// </summary>
82         public bool IgnoreCase
83         {
84             get { return caseInsensitive; }
85             set { caseInsensitive = value; }
86         }
87
88         /// <summary>
89         /// Gets and sets a flag indicating that arrays should be
90         /// compared as collections, without regard to their shape.
91         /// </summary>
92         public bool CompareAsCollection
93         {
94             get { return compareAsCollection; }
95             set { compareAsCollection = value; }
96         }
97
98         /// <summary>
99         /// Gets the list of external comparers to be used to
100         /// test for equality. They are applied to members of
101         /// collections, in place of NUnit's own logic.
102         /// </summary>
103         public IList<EqualityAdapter> ExternalComparers
104         {
105             get { return externalComparers; }
106         }
107
108         // TODO: Define some sort of FailurePoint struct or otherwise
109         // eliminate the type-unsafeness of the current approach
110
111         /// <summary>
112         /// Gets the list of failure points for the last Match performed.
113         /// The list consists of objects to be interpreted by the caller.
114         /// This generally means that the caller may only make use of
115         /// objects it has placed on the list at a particular depthy.
116         /// </summary>
117         public IList<FailurePoint> FailurePoints
118         {
119             get { return failurePoints; }
120         }
121
122 #if !NETCF
123         /// <summary>
124         /// Flags the comparer to include <see cref="DateTimeOffset.Offset"/>
125         /// property in comparison of two <see cref="DateTimeOffset"/> values.
126         /// </summary>
127         /// <remarks>
128         /// Using this modifier does not allow to use the <see cref="Tolerance"/>
129         /// modifier.
130         /// </remarks>
131         public bool WithSameOffset { get; set; }
132 #endif
133                   #endregion
134
135         #region Public Methods
136         /// <summary>
137         /// Compares two objects for equality within a tolerance.
138         /// </summary>
139         public bool AreEqual(object x, object y, ref Tolerance tolerance)
140         {
141             this.failurePoints = new List<FailurePoint>();
142
143             if (x == null && y == null)
144                 return true;
145
146             if (x == null || y == null)
147                 return false;
148
149             if (object.ReferenceEquals(x, y))
150                 return true;
151
152             Type xType = x.GetType();
153             Type yType = y.GetType();
154
155             Type xGenericTypeDefinition = xType.GetTypeInfo().IsGenericType ? xType.GetGenericTypeDefinition() : null;
156             Type yGenericTypeDefinition = yType.GetTypeInfo().IsGenericType ? yType.GetGenericTypeDefinition() : null;
157
158             EqualityAdapter externalComparer = GetExternalComparer(x, y);
159             if (externalComparer != null)
160                 return externalComparer.AreEqual(x, y);
161
162             if (xType.IsArray && yType.IsArray && !compareAsCollection)
163                 return ArraysEqual((Array)x, (Array)y, ref tolerance);
164
165             if (x is IDictionary && y is IDictionary)
166                 return DictionariesEqual((IDictionary)x, (IDictionary)y, ref tolerance);
167             
168             // Issue #70 - EquivalentTo isn't compatible with IgnoreCase for dictionaries
169             if (x is DictionaryEntry && y is DictionaryEntry)
170                 return DictionaryEntriesEqual((DictionaryEntry)x, (DictionaryEntry)y, ref tolerance);
171
172             // IDictionary<,> will eventually try to compare it's key value pairs when using CollectionTally
173             if (xGenericTypeDefinition == typeof(KeyValuePair<,>) &&
174                 yGenericTypeDefinition == typeof(KeyValuePair<,>))
175             {
176                 var keyTolerance = Tolerance.Exact;
177                 object xKey = xType.GetProperty("Key").GetValue(x, null);
178                 object yKey = yType.GetProperty("Key").GetValue(y, null);
179                 object xValue = xType.GetProperty("Value").GetValue(x, null);
180                 object yValue = yType.GetProperty("Value").GetValue(y, null);
181
182                 return AreEqual(xKey, yKey, ref keyTolerance) && AreEqual(xValue, yValue, ref tolerance);
183             }
184
185             //if (x is ICollection && y is ICollection)
186             //    return CollectionsEqual((ICollection)x, (ICollection)y, ref tolerance);
187
188             if (x is string && y is string)
189                 return StringsEqual((string)x, (string)y);
190
191             if (x is Stream && y is Stream)
192                 return StreamsEqual((Stream)x, (Stream)y);
193
194             if ( x is char && y is char )
195                 return CharsEqual( (char)x, (char)y );
196
197 #if !PORTABLE
198             if (x is DirectoryInfo && y is DirectoryInfo)
199                 return DirectoriesEqual((DirectoryInfo)x, (DirectoryInfo)y);
200 #endif
201
202             if (Numerics.IsNumericType(x) && Numerics.IsNumericType(y))
203                 return Numerics.AreEqual(x, y, ref tolerance);
204
205 #if !NETCF
206             if (x is DateTimeOffset && y is DateTimeOffset)
207             {
208                 bool result;
209
210                 DateTimeOffset xAsOffset = (DateTimeOffset)x;
211                 DateTimeOffset yAsOffset = (DateTimeOffset)y;
212
213                 if (tolerance != null && tolerance.Value is TimeSpan)
214                 {
215                     TimeSpan amount = (TimeSpan)tolerance.Value;
216                     result = (xAsOffset - yAsOffset).Duration() <= amount;
217                 }
218                 else
219                 {
220                     result = xAsOffset == yAsOffset;
221                 }
222
223                 if (result && WithSameOffset)
224                 {
225                     result = xAsOffset.Offset == yAsOffset.Offset;
226                 }
227
228                 return result;
229             }
230 #endif
231
232             if (tolerance != null && tolerance.Value is TimeSpan)
233             {
234                 TimeSpan amount = (TimeSpan)tolerance.Value;
235
236                 if (x is DateTime && y is DateTime)
237                     return ((DateTime)x - (DateTime)y).Duration() <= amount;
238
239                 if (x is TimeSpan && y is TimeSpan)
240                     return ((TimeSpan)x - (TimeSpan)y).Duration() <= amount;
241             }
242
243             MethodInfo equals = FirstImplementsIEquatableOfSecond(xType, yType);
244             if (equals != null)
245                 return InvokeFirstIEquatableEqualsSecond(x, y, equals);
246             if (xType != yType && (equals = FirstImplementsIEquatableOfSecond(yType, xType)) != null)
247                 return InvokeFirstIEquatableEqualsSecond(y, x, equals);
248
249             if (x is IEnumerable && y is IEnumerable)
250                 return EnumerablesEqual((IEnumerable) x, (IEnumerable) y, ref tolerance);
251
252             return x.Equals(y);
253         }
254
255         private static MethodInfo FirstImplementsIEquatableOfSecond(Type first, Type second)
256         {
257             var pair = new KeyValuePair<Type, MethodInfo>();
258
259             foreach (var xEquatableArgument in GetEquatableGenericArguments(first))
260                 if (xEquatableArgument.Key.IsAssignableFrom(second))
261                     if (pair.Key == null || pair.Key.IsAssignableFrom(xEquatableArgument.Key))
262                         pair = xEquatableArgument;
263
264             return pair.Value;
265         }
266
267         private static IList<KeyValuePair<Type, MethodInfo>> GetEquatableGenericArguments(Type type)
268         {
269             // NOTE: Original implementation used Array.ConvertAll and
270             // Array.FindAll, which don't exist in the compact framework.
271             var genericArgs = new List<KeyValuePair<Type, MethodInfo>>();
272
273             foreach (Type @interface in type.GetInterfaces())
274             {
275                 if (@interface.GetTypeInfo().IsGenericType && @interface.GetGenericTypeDefinition().Equals(typeof(IEquatable<>)))
276                 {
277                     genericArgs.Add(new KeyValuePair<Type, MethodInfo>(
278                         @interface.GetGenericArguments()[0], @interface.GetMethod("Equals")));
279                 }
280             }
281
282             return genericArgs;
283         }
284
285         private static bool InvokeFirstIEquatableEqualsSecond(object first, object second, MethodInfo equals)
286         {
287             return equals != null ? (bool)equals.Invoke(first, new object[] { second }) : false;
288         }
289         
290         #endregion
291
292         #region Helper Methods
293
294         private EqualityAdapter GetExternalComparer(object x, object y)
295         {
296             foreach (EqualityAdapter adapter in externalComparers)
297                 if (adapter.CanCompare(x, y))
298                     return adapter;
299
300             return null;
301         }
302
303         /// <summary>
304         /// Helper method to compare two arrays
305         /// </summary>
306         private bool ArraysEqual(Array x, Array y, ref Tolerance tolerance)
307         {
308             int rank = x.Rank;
309
310             if (rank != y.Rank)
311                 return false;
312
313             for (int r = 1; r < rank; r++)
314                 if (x.GetLength(r) != y.GetLength(r))
315                     return false;
316
317             return EnumerablesEqual((IEnumerable)x, (IEnumerable)y, ref tolerance);
318         }
319
320         private bool DictionariesEqual(IDictionary x, IDictionary y, ref Tolerance tolerance)
321         {
322             if (x.Count != y.Count)
323                 return false;
324  
325             CollectionTally tally = new CollectionTally(this, x.Keys);
326             if (!tally.TryRemove(y.Keys) || tally.Count > 0)
327                 return false;
328
329             foreach (object key in x.Keys)
330                 if (!AreEqual(x[key], y[key], ref tolerance))
331                     return false;
332  
333             return true;
334         }
335
336         private bool DictionaryEntriesEqual(DictionaryEntry x, DictionaryEntry y, ref Tolerance tolerance)
337         {
338             var keyTolerance = Tolerance.Exact;
339             return AreEqual(x.Key, y.Key, ref keyTolerance) && AreEqual(x.Value, y.Value, ref tolerance);
340         }
341
342         private bool CollectionsEqual(ICollection x, ICollection y, ref Tolerance tolerance)
343         {
344             IEnumerator expectedEnum = null;
345             IEnumerator actualEnum = null;
346
347             try
348             {
349                 expectedEnum = x.GetEnumerator();
350                 actualEnum = y.GetEnumerator();
351                 int count;
352                 for (count = 0; ; count++)
353                 {
354                     bool expectedHasData = expectedEnum.MoveNext();
355                     bool actualHasData = actualEnum.MoveNext();
356
357                     if (!expectedHasData && !actualHasData)
358                         return true;
359
360                     if (expectedHasData != actualHasData ||
361                         !AreEqual(expectedEnum.Current, actualEnum.Current, ref tolerance))
362                     {
363                         FailurePoint fp = new FailurePoint();
364                         fp.Position = count;
365                         fp.ExpectedHasData = expectedHasData;
366                         if (expectedHasData)
367                             fp.ExpectedValue = expectedEnum.Current;
368                         fp.ActualHasData = actualHasData;
369                         if (actualHasData)
370                             fp.ActualValue = actualEnum.Current;
371                         failurePoints.Insert(0, fp);
372                         return false;
373                     }
374                 }
375             }
376             finally
377             {
378                 var expectedDisposable = expectedEnum as IDisposable;
379                 if (expectedDisposable != null) expectedDisposable.Dispose();
380
381                 var actualDisposable = actualEnum as IDisposable;
382                 if (actualDisposable != null) actualDisposable.Dispose();
383
384             }
385         }
386
387         private bool StringsEqual(string x, string y)
388         {
389             string s1 = caseInsensitive ? x.ToLower() : x;
390             string s2 = caseInsensitive ? y.ToLower() : y;
391
392             return s1.Equals(s2);
393         }
394
395         private bool CharsEqual(char x, char y)
396         {
397             char c1 = caseInsensitive ? Char.ToLower(x) : x;
398             char c2 = caseInsensitive ? Char.ToLower(y) : y;
399
400             return c1 == c2;
401         }
402
403         private bool EnumerablesEqual(IEnumerable x, IEnumerable y, ref Tolerance tolerance)
404         {
405             IEnumerator expectedEnum = null;
406             IEnumerator actualEnum = null;
407
408             try
409             {
410                 expectedEnum = x.GetEnumerator();
411                 actualEnum = y.GetEnumerator();
412
413                 int count;
414                 for (count = 0; ; count++)
415                 {
416                     bool expectedHasData = expectedEnum.MoveNext();
417                     bool actualHasData = actualEnum.MoveNext();
418
419                     if (!expectedHasData && !actualHasData)
420                         return true;
421
422                     if (expectedHasData != actualHasData ||
423                         !AreEqual(expectedEnum.Current, actualEnum.Current, ref tolerance))
424                     {
425                         FailurePoint fp = new FailurePoint();
426                         fp.Position = count;
427                         fp.ExpectedHasData = expectedHasData;
428                         if (expectedHasData)
429                             fp.ExpectedValue = expectedEnum.Current;
430                         fp.ActualHasData = actualHasData;
431                         if (actualHasData)
432                             fp.ActualValue = actualEnum.Current;
433                         failurePoints.Insert(0, fp);
434                         return false;
435                     }
436                 }
437             }
438             finally
439             {
440                 var expectedDisposable = expectedEnum as IDisposable;
441                 if (expectedDisposable != null) expectedDisposable.Dispose();
442
443                 var actualDisposable = actualEnum as IDisposable;
444                 if (actualDisposable != null) actualDisposable.Dispose();
445
446             }
447         }
448
449 #if !PORTABLE
450         /// <summary>
451         /// Method to compare two DirectoryInfo objects
452         /// </summary>
453         /// <param name="x">first directory to compare</param>
454         /// <param name="y">second directory to compare</param>
455         /// <returns>true if equivalent, false if not</returns>
456         private static bool DirectoriesEqual(DirectoryInfo x, DirectoryInfo y)
457         {
458             // Do quick compares first
459             if (x.Attributes != y.Attributes ||
460                 x.CreationTime != y.CreationTime ||
461                 x.LastAccessTime != y.LastAccessTime)
462             {
463                 return false;
464             }
465
466             // TODO: Find a cleaner way to do this
467             return new SamePathConstraint(x.FullName).ApplyTo(y.FullName).IsSuccess;
468         }
469 #endif
470
471         private bool StreamsEqual(Stream x, Stream y)
472         {
473             if (x == y) return true;
474
475             if (!x.CanRead)
476                 throw new ArgumentException("Stream is not readable", "expected");
477             if (!y.CanRead)
478                 throw new ArgumentException("Stream is not readable", "actual");
479             if (!x.CanSeek)
480                 throw new ArgumentException("Stream is not seekable", "expected");
481             if (!y.CanSeek)
482                 throw new ArgumentException("Stream is not seekable", "actual");
483
484             if (x.Length != y.Length) return false;
485
486             byte[] bufferExpected = new byte[BUFFER_SIZE];
487             byte[] bufferActual = new byte[BUFFER_SIZE];
488
489             BinaryReader binaryReaderExpected = new BinaryReader(x);
490             BinaryReader binaryReaderActual = new BinaryReader(y);
491
492             long expectedPosition = x.Position;
493             long actualPosition = y.Position;
494
495             try
496             {
497                 binaryReaderExpected.BaseStream.Seek(0, SeekOrigin.Begin);
498                 binaryReaderActual.BaseStream.Seek(0, SeekOrigin.Begin);
499
500                 for (long readByte = 0; readByte < x.Length; readByte += BUFFER_SIZE)
501                 {
502                     binaryReaderExpected.Read(bufferExpected, 0, BUFFER_SIZE);
503                     binaryReaderActual.Read(bufferActual, 0, BUFFER_SIZE);
504
505                     for (int count = 0; count < BUFFER_SIZE; ++count)
506                     {
507                         if (bufferExpected[count] != bufferActual[count])
508                         {
509                             FailurePoint fp = new FailurePoint();
510                             fp.Position = readByte + count;
511                             fp.ExpectedHasData = true;
512                             fp.ExpectedValue = bufferExpected[count];
513                             fp.ActualHasData = true;
514                             fp.ActualValue = bufferActual[count];
515                             failurePoints.Insert(0, fp);
516                             return false;
517                         }
518                     }
519                 }
520             }
521             finally
522             {
523                 x.Position = expectedPosition;
524                 y.Position = actualPosition;
525             }
526
527             return true;
528         }
529         
530         #endregion
531
532         #region Nested FailurePoint Class
533
534         /// <summary>
535         /// FailurePoint class represents one point of failure
536         /// in an equality test.
537         /// </summary>
538         public class FailurePoint
539         {
540             /// <summary>
541             /// The location of the failure
542             /// </summary>
543             public long Position;
544
545             /// <summary>
546             /// The expected value
547             /// </summary>
548             public object ExpectedValue;
549
550             /// <summary>
551             /// The actual value
552             /// </summary>
553             public object ActualValue;
554
555             /// <summary>
556             /// Indicates whether the expected value is valid
557             /// </summary>
558             public bool ExpectedHasData;
559
560             /// <summary>
561             /// Indicates whether the actual value is valid
562             /// </summary>
563             public bool ActualHasData;
564         }
565
566         #endregion
567     }
568 }