#### /src/NUnit/framework/Constraints/Tolerance.cs

#
C# | 225 lines | 117 code | 21 blank | 87 comment | 5 complexity | f0c5425961d59d91afe59bd6492a3f68 MD5 | raw file
```  1// ****************************************************************
3// This is free software licensed under the NUnit license. You may
4// obtain a copy of the license at http://nunit.org/
5// ****************************************************************
6
7using System;
8
9namespace NUnit.Framework.Constraints
10{
11    /// <summary>
12    /// Modes in which the tolerance value for a comparison can
13    /// be interpreted.
14    /// </summary>
15    public enum ToleranceMode
16    {
17        /// <summary>
18        /// The tolerance was created with a value, without specifying
19        /// how the value would be used. This is used to prevent setting
20        /// the mode more than once and is generally changed to Linear
21        /// upon execution of the test.
22        /// </summary>
23        None,
24        /// <summary>
25        /// The tolerance is used as a numeric range within which
26        /// two compared values are considered to be equal.
27        /// </summary>
28        Linear,
29        /// <summary>
30        /// Interprets the tolerance as the percentage by which
31        /// the two compared values my deviate from each other.
32        /// </summary>
33        Percent,
34        /// <summary>
35        /// Compares two values based in their distance in
36        /// representable numbers.
37        /// </summary>
38        Ulps
39    }
40
41    /// <summary>
42    /// The Tolerance class generalizes the notion of a tolerance
43    /// within which an equality test succeeds. Normally, it is
44    /// used with numeric types, but it can be used with any
45    /// type that supports taking a difference between two
46    /// objects and comparing that difference to a value.
47    /// </summary>
48    public class Tolerance
49    {
50        private ToleranceMode mode;
51        private object amount;
52
53        private static readonly string ModeMustFollowTolerance =
54            "Tolerance amount must be specified before setting mode";
55        private static readonly string MultipleToleranceModes =
56            "Tried to use multiple tolerance modes at the same time";
57        private static readonly string NumericToleranceRequired =
58            "A numeric tolerance is required";
59
60        /// <summary>
61        /// Returns an empty Tolerance object, equivalent to
62        /// specifying an exact match.
63        /// </summary>
64        public static Tolerance Empty
65        {
66            get { return new Tolerance(0, ToleranceMode.None); }
67        }
68
69        /// <summary>
70        /// Constructs a linear tolerance of a specdified amount
71        /// </summary>
72        public Tolerance(object amount) : this(amount, ToleranceMode.Linear) { }
73
74        /// <summary>
75        /// Constructs a tolerance given an amount and ToleranceMode
76        /// </summary>
77        private Tolerance(object amount, ToleranceMode mode)
78        {
79            this.amount = amount;
80            this.mode = mode;
81        }
82
83        /// <summary>
84        /// Gets the ToleranceMode for the current Tolerance
85        /// </summary>
86        public ToleranceMode Mode
87        {
88            get { return this.mode; }
89        }
90
91
92        /// <summary>
93        /// Tests that the current Tolerance is linear with a
94        /// numeric value, throwing an exception if it is not.
95        /// </summary>
96        private void CheckLinearAndNumeric()
97        {
98            if (mode != ToleranceMode.Linear)
99                throw new InvalidOperationException(mode == ToleranceMode.None
100                    ? ModeMustFollowTolerance
101                    : MultipleToleranceModes);
102
103            if (!Numerics.IsNumericType(amount))
104                throw new InvalidOperationException(NumericToleranceRequired);
105        }
106
107        /// <summary>
108        /// Gets the value of the current Tolerance instance.
109        /// </summary>
110        public object Value
111        {
112            get { return this.amount; }
113        }
114
115        /// <summary>
116        /// Returns a new tolerance, using the current amount as a percentage.
117        /// </summary>
118        public Tolerance Percent
119        {
120            get
121            {
122                CheckLinearAndNumeric();
123                return new Tolerance(this.amount, ToleranceMode.Percent);
124            }
125        }
126
127        /// <summary>
128        /// Returns a new tolerance, using the current amount in Ulps.
129        /// </summary>
130        public Tolerance Ulps
131        {
132            get
133            {
134                CheckLinearAndNumeric();
135                return new Tolerance(this.amount, ToleranceMode.Ulps);
136            }
137        }
138
139        /// <summary>
140        /// Returns a new tolerance with a TimeSpan as the amount, using
141        /// the current amount as a number of days.
142        /// </summary>
143        public Tolerance Days
144        {
145            get
146            {
147                CheckLinearAndNumeric();
148                return new Tolerance(TimeSpan.FromDays(Convert.ToDouble(amount)));
149            }
150        }
151
152        /// <summary>
153        /// Returns a new tolerance with a TimeSpan as the amount, using
154        /// the current amount as a number of hours.
155        /// </summary>
156        public Tolerance Hours
157        {
158            get
159            {
160                CheckLinearAndNumeric();
161                return new Tolerance(TimeSpan.FromHours(Convert.ToDouble(amount)));
162            }
163        }
164
165        /// <summary>
166        /// Returns a new tolerance with a TimeSpan as the amount, using
167        /// the current amount as a number of minutes.
168        /// </summary>
169        public Tolerance Minutes
170        {
171            get
172            {
173                CheckLinearAndNumeric();
174                return new Tolerance(TimeSpan.FromMinutes(Convert.ToDouble(amount)));
175            }
176        }
177
178        /// <summary>
179        /// Returns a new tolerance with a TimeSpan as the amount, using
180        /// the current amount as a number of seconds.
181        /// </summary>
182        public Tolerance Seconds
183        {
184            get
185            {
186                CheckLinearAndNumeric();
187                return new Tolerance(TimeSpan.FromSeconds(Convert.ToDouble(amount)));
188            }
189        }
190
191        /// <summary>
192        /// Returns a new tolerance with a TimeSpan as the amount, using
193        /// the current amount as a number of milliseconds.
194        /// </summary>
195        public Tolerance Milliseconds
196        {
197            get
198            {
199                CheckLinearAndNumeric();
200                return new Tolerance(TimeSpan.FromMilliseconds(Convert.ToDouble(amount)));
201            }
202        }
203
204        /// <summary>
205        /// Returns a new tolerance with a TimeSpan as the amount, using
206        /// the current amount as a number of clock ticks.
207        /// </summary>
208        public Tolerance Ticks
209        {
210            get
211            {
212                CheckLinearAndNumeric();
213                return new Tolerance(TimeSpan.FromTicks(Convert.ToInt64(amount)));
214            }
215        }
216
217        /// <summary>
218        /// Returns true if the current tolerance is empty.
219        /// </summary>
220        public bool IsEmpty
221        {
222            get { return mode == ToleranceMode.None; }
223        }
224    }
225}
```