]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - tests/PHPUnit/PHPUnit/Framework/Constraint/IsEqual.php
Add .gitignore
[Github/sugarcrm.git] / tests / PHPUnit / PHPUnit / Framework / Constraint / IsEqual.php
1 <?php
2 /**
3  * PHPUnit
4  *
5  * Copyright (c) 2002-2011, Sebastian Bergmann <sebastian@phpunit.de>.
6  * All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  *
12  *   * Redistributions of source code must retain the above copyright
13  *     notice, this list of conditions and the following disclaimer.
14  *
15  *   * Redistributions in binary form must reproduce the above copyright
16  *     notice, this list of conditions and the following disclaimer in
17  *     the documentation and/or other materials provided with the
18  *     distribution.
19  *
20  *   * Neither the name of Sebastian Bergmann nor the names of his
21  *     contributors may be used to endorse or promote products derived
22  *     from this software without specific prior written permission.
23  *
24  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
25  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
26  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
27  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
28  * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
29  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
30  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
31  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
32  * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
33  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
34  * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
35  * POSSIBILITY OF SUCH DAMAGE.
36  *
37  * @package    PHPUnit
38  * @subpackage Framework_Constraint
39  * @author     Kore Nordmann <kn@ez.no>
40  * @author     Sebastian Bergmann <sebastian@phpunit.de>
41  * @copyright  2002-2011 Sebastian Bergmann <sebastian@phpunit.de>
42  * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
43  * @link       http://www.phpunit.de/
44  * @since      File available since Release 3.0.0
45  */
46
47 /**
48  * Constraint that checks if one value is equal to another.
49  *
50  * Equality is checked with PHP's == operator, the operator is explained in
51  * detail at {@url http://www.php.net/manual/en/types.comparisons.php}.
52  * Two values are equal if they have the same value disregarding type.
53  *
54  * The expected value is passed in the constructor.
55  *
56  * @package    PHPUnit
57  * @subpackage Framework_Constraint
58  * @author     Kore Nordmann <kn@ez.no>
59  * @author     Sebastian Bergmann <sebastian@phpunit.de>
60  * @copyright  2002-2011 Sebastian Bergmann <sebastian@phpunit.de>
61  * @license    http://www.opensource.org/licenses/bsd-license.php  BSD License
62  * @version    Release: 3.5.13
63  * @link       http://www.phpunit.de/
64  * @since      Class available since Release 3.0.0
65  */
66 class PHPUnit_Framework_Constraint_IsEqual extends PHPUnit_Framework_Constraint
67 {
68     /**
69      * @var mixed
70      */
71     protected $value;
72
73     /**
74      * @var float
75      */
76     protected $delta = 0;
77
78     /**
79      * @var integer
80      */
81     protected $maxDepth = 10;
82
83     /**
84      * @var boolean
85      */
86     protected $canonicalize = FALSE;
87
88     /**
89      * @var boolean
90      */
91     protected $ignoreCase = FALSE;
92
93     /**
94      * @param mixed   $value
95      * @param float   $delta
96      * @param integer $maxDepth
97      * @param boolean $canonicalize
98      * @param boolean $ignoreCase
99      */
100     public function __construct($value, $delta = 0, $maxDepth = 10, $canonicalize = FALSE, $ignoreCase = FALSE)
101     {
102         if (!is_numeric($delta)) {
103             throw PHPUnit_Util_InvalidArgumentHelper::factory(2, 'numeric');
104         }
105
106         if (!is_int($maxDepth)) {
107             throw PHPUnit_Util_InvalidArgumentHelper::factory(3, 'integer');
108         }
109
110         if (!is_bool($canonicalize)) {
111             throw PHPUnit_Util_InvalidArgumentHelper::factory(4, 'boolean');
112         }
113
114         if (!is_bool($ignoreCase)) {
115             throw PHPUnit_Util_InvalidArgumentHelper::factory(5, 'boolean');
116         }
117
118         $this->value        = $value;
119         $this->delta        = $delta;
120         $this->maxDepth     = $maxDepth;
121         $this->canonicalize = $canonicalize;
122         $this->ignoreCase   = $ignoreCase;
123     }
124
125     /**
126      * Evaluates the constraint for parameter $other. Returns TRUE if the
127      * constraint is met, FALSE otherwise.
128      *
129      * @param mixed $other Value or object to evaluate.
130      * @return bool
131      */
132     public function evaluate($other)
133     {
134         return $this->recursiveComparison($this->value, $other);
135     }
136
137     /**
138      * @param   mixed   $other The value passed to evaluate() which failed the
139      *                         constraint check.
140      * @param   string  $description A string with extra description of what was
141      *                               going on while the evaluation failed.
142      * @param   boolean $not Flag to indicate negation.
143      * @throws  PHPUnit_Framework_ExpectationFailedException
144      */
145     public function fail($other, $description, $not = FALSE)
146     {
147         $failureDescription = $this->failureDescription(
148           $other,
149           $description,
150           $not
151         );
152
153         if (!$not) {
154             if ($this->value instanceof DOMDocument) {
155                 $value = $this->domToText($this->value);
156             } else {
157                 $value = $this->value;
158             }
159
160             if ($other instanceof DOMDocument) {
161                 $other = $this->domToText($other);
162             }
163
164             throw new PHPUnit_Framework_ExpectationFailedException(
165               $failureDescription,
166               PHPUnit_Framework_ComparisonFailure::diffEqual($value, $other),
167               $description
168             );
169         } else {
170             throw new PHPUnit_Framework_ExpectationFailedException(
171               $failureDescription,
172               NULL
173             );
174         }
175     }
176
177     /**
178      * Returns a string representation of the constraint.
179      *
180      * @return string
181      */
182     public function toString()
183     {
184         $delta = '';
185
186         if (is_string($this->value)) {
187             if (strpos($this->value, "\n") !== FALSE) {
188                 return 'is equal to <text>';
189             } else {
190                 return sprintf(
191                   'is equal to <string:%s>',
192
193                   $this->value
194                 );
195             }
196         } else {
197             if ($this->delta != 0) {
198                 $delta = sprintf(
199                   ' with delta <%F>',
200
201                   $this->delta
202                 );
203             }
204
205             return sprintf(
206               'is equal to %s%s',
207
208               PHPUnit_Util_Type::toString($this->value),
209               $delta
210             );
211         }
212     }
213
214     /**
215      * Perform the actual recursive comparision of two values
216      *
217      * @param mixed $a First value
218      * @param mixed $b Second value
219      * @param int $depth Depth
220      * @return bool
221      */
222     protected function recursiveComparison($a, $b, $depth = 0)
223     {
224         if ($a === $b) {
225             return TRUE;
226         }
227
228         if ($depth >= $this->maxDepth) {
229             return TRUE;
230         }
231
232         if (is_numeric($a) XOR is_numeric($b)) {
233             return FALSE;
234         }
235
236         if (is_array($a) XOR is_array($b)) {
237             return FALSE;
238         }
239
240         if (is_object($a) XOR is_object($b)) {
241             return FALSE;
242         }
243
244         if ($a instanceof SplObjectStorage XOR $b instanceof SplObjectStorage) {
245             return FALSE;
246         }
247
248         if ($a instanceof SplObjectStorage) {
249             foreach ($a as $object) {
250                 if (!$b->contains($object)) {
251                     return FALSE;
252                 }
253             }
254
255             foreach ($b as $object) {
256                 if (!$a->contains($object)) {
257                     return FALSE;
258                 }
259             }
260
261             return TRUE;
262         }
263
264         if ($a instanceof DOMDocument || $b instanceof DOMDocument) {
265             if (!$a instanceof DOMDocument) {
266                 $_a = new DOMDocument;
267                 $_a->preserveWhiteSpace = FALSE;
268                 $_a->loadXML($a);
269                 $a = $_a;
270                 unset($_a);
271             }
272
273             if (!$b instanceof DOMDocument) {
274                 $_b = new DOMDocument;
275                 $_b->preserveWhiteSpace = FALSE;
276                 $_b->loadXML($b);
277                 $b = $_b;
278                 unset($_b);
279             }
280
281             return $a->C14N() == $b->C14N();
282         }
283
284         if (is_object($a) && is_object($b) &&
285            (get_class($a) !== get_class($b))) {
286             return FALSE;
287         }
288
289         // Normal comparision for scalar values.
290         if ((!is_array($a) && !is_object($a)) ||
291             (!is_array($b) && !is_object($b))) {
292             if (is_numeric($a) && is_numeric($b)) {
293                 // Optionally apply delta on numeric values.
294                 return $this->numericComparison($a, $b);
295             }
296
297             if (is_string($a) && is_string($b)) {
298                 if ($this->canonicalize && PHP_EOL != "\n") {
299                     $a = str_replace(PHP_EOL, "\n", $a);
300                     $b = str_replace(PHP_EOL, "\n", $b);
301                 }
302
303                 if ($this->ignoreCase) {
304                     $a = strtolower($a);
305                     $b = strtolower($b);
306                 }
307             }
308
309             return ($a == $b);
310         }
311
312         if (is_object($a)) {
313             $isMock = $a instanceof PHPUnit_Framework_MockObject_MockObject;
314             $a      = (array)$a;
315             $b      = (array)$b;
316
317             if ($isMock) {
318                 unset($a["\0*\0invocationMocker"]);
319
320                 if (isset($b["\0*\0invocationMocker"])) {
321                     unset($b["\0*\0invocationMocker"]);
322                 }
323             }
324         }
325
326         if ($this->canonicalize) {
327             sort($a);
328             sort($b);
329         }
330
331         $keysInB = array_flip(array_keys($b));
332
333         foreach ($a as $key => $v) {
334             if (!isset($keysInB[$key])) {
335                 // Abort on missing key in $b.
336                 return FALSE;
337             }
338
339             if (!$this->recursiveComparison($a[$key], $b[$key], $depth + 1)) {
340                 // FALSE, if child comparision fails.
341                 return FALSE;
342             }
343
344             // Unset key to check whether all keys of b are compared.
345             unset($b[$key]);
346         }
347
348         if (count($b)) {
349             // There is something in $b, that is missing in $a.
350             return FALSE;
351         }
352
353         return TRUE;
354     }
355
356     /**
357      * Compares two numeric values - use delta if applicable.
358      *
359      * @param mixed $a
360      * @param mixed $b
361      * @return bool
362      */
363     protected function numericComparison($a, $b)
364     {
365         if ($this->delta === FALSE) {
366             return ($a == $b);
367         } else {
368             return (abs($a - $b) <= $this->delta);
369         }
370     }
371
372     /**
373      * Returns the normalized, whitespace-cleaned, and indented textual
374      * representation of a DOMDocument.
375      *
376      * @param DOMDocument $document
377      * @return string
378      */
379     protected function domToText(DOMDocument $document)
380     {
381         $document->formatOutput = TRUE;
382         $document->normalizeDocument();
383
384         return $document->saveXML();
385     }
386 }