source: trunk/server/www/vendors/simpletest/expectation.php @ 6

Last change on this file since 6 was 6, checked in by sander, 12 years ago

Added SimpleTest? test framework

File size: 26.6 KB
Line 
1<?php
2/**
3 *    base include file for SimpleTest
4 *    @package    SimpleTest
5 *    @subpackage    UnitTester
6 *    @version    $Id: expectation.php 1723 2008-04-08 00:34:10Z lastcraft $
7 */
8
9/**#@+
10 *    include other SimpleTest class files
11 */
12require_once(dirname(__FILE__) . '/dumper.php');
13require_once(dirname(__FILE__) . '/compatibility.php');
14/**#@-*/
15
16/**
17 *    Assertion that can display failure information.
18 *    Also includes various helper methods.
19 *    @package SimpleTest
20 *    @subpackage UnitTester
21 *    @abstract
22 */
23class SimpleExpectation {
24    var $_dumper = false;
25    var $_message;
26
27    /**
28     *    Creates a dumper for displaying values and sets
29     *    the test message.
30     *    @param string $message    Customised message on failure.
31     */
32    function SimpleExpectation($message = '%s') {
33        $this->_message = $message;
34    }
35
36    /**
37     *    Tests the expectation. True if correct.
38     *    @param mixed $compare        Comparison value.
39     *    @return boolean              True if correct.
40     *    @access public
41     *    @abstract
42     */
43    function test($compare) {
44    }
45
46    /**
47     *    Returns a human readable test message.
48     *    @param mixed $compare      Comparison value.
49     *    @return string             Description of success
50     *                               or failure.
51     *    @access public
52     *    @abstract
53     */
54    function testMessage($compare) {
55    }
56
57    /**
58     *    Overlays the generated message onto the stored user
59     *    message. An additional message can be interjected.
60     *    @param mixed $compare        Comparison value.
61     *    @param SimpleDumper $dumper  For formatting the results.
62     *    @return string               Description of success
63     *                                 or failure.
64     *    @access public
65     */
66    function overlayMessage($compare, $dumper) {
67        $this->_dumper = $dumper;
68        return sprintf($this->_message, $this->testMessage($compare));
69    }
70
71    /**
72     *    Accessor for the dumper.
73     *    @return SimpleDumper    Current value dumper.
74     *    @access protected
75     */
76    function &_getDumper() {
77        if (! $this->_dumper) {
78            $dumper = &new SimpleDumper();
79            return $dumper;
80        }
81        return $this->_dumper;
82    }
83
84    /**
85     *    Test to see if a value is an expectation object.
86     *    A useful utility method.
87     *    @param mixed $expectation    Hopefully an Epectation
88     *                                 class.
89     *    @return boolean              True if descended from
90     *                                 this class.
91     *    @access public
92     *    @static
93     */
94    function isExpectation($expectation) {
95        return is_object($expectation) &&
96                SimpleTestCompatibility::isA($expectation, 'SimpleExpectation');
97    }
98}
99
100/**
101 *    A wildcard expectation always matches.
102 *    @package SimpleTest
103 *    @subpackage MockObjects
104 */
105class AnythingExpectation extends SimpleExpectation {
106
107    /**
108     *    Tests the expectation. Always true.
109     *    @param mixed $compare  Ignored.
110     *    @return boolean        True.
111     *    @access public
112     */
113    function test($compare) {
114        return true;
115    }
116
117    /**
118     *    Returns a human readable test message.
119     *    @param mixed $compare      Comparison value.
120     *    @return string             Description of success
121     *                               or failure.
122     *    @access public
123     */
124    function testMessage($compare) {
125        $dumper = &$this->_getDumper();
126        return 'Anything always matches [' . $dumper->describeValue($compare) . ']';
127    }
128}
129
130/**
131 *    An expectation that never matches.
132 *    @package SimpleTest
133 *    @subpackage MockObjects
134 */
135class FailedExpectation extends SimpleExpectation {
136
137    /**
138     *    Tests the expectation. Always false.
139     *    @param mixed $compare  Ignored.
140     *    @return boolean        True.
141     *    @access public
142     */
143    function test($compare) {
144        return false;
145    }
146
147    /**
148     *    Returns a human readable test message.
149     *    @param mixed $compare      Comparison value.
150     *    @return string             Description of failure.
151     *    @access public
152     */
153    function testMessage($compare) {
154        $dumper = &$this->_getDumper();
155        return 'Failed expectation never matches [' . $dumper->describeValue($compare) . ']';
156    }
157}
158
159/**
160 *    An expectation that passes on boolean true.
161 *    @package SimpleTest
162 *    @subpackage MockObjects
163 */
164class TrueExpectation extends SimpleExpectation {
165
166    /**
167     *    Tests the expectation.
168     *    @param mixed $compare  Should be true.
169     *    @return boolean        True on match.
170     *    @access public
171     */
172    function test($compare) {
173        return (boolean)$compare;
174    }
175
176    /**
177     *    Returns a human readable test message.
178     *    @param mixed $compare      Comparison value.
179     *    @return string             Description of success
180     *                               or failure.
181     *    @access public
182     */
183    function testMessage($compare) {
184        $dumper = &$this->_getDumper();
185        return 'Expected true, got [' . $dumper->describeValue($compare) . ']';
186    }
187}
188
189/**
190 *    An expectation that passes on boolean false.
191 *    @package SimpleTest
192 *    @subpackage MockObjects
193 */
194class FalseExpectation extends SimpleExpectation {
195
196    /**
197     *    Tests the expectation.
198     *    @param mixed $compare  Should be false.
199     *    @return boolean        True on match.
200     *    @access public
201     */
202    function test($compare) {
203        return ! (boolean)$compare;
204    }
205
206    /**
207     *    Returns a human readable test message.
208     *    @param mixed $compare      Comparison value.
209     *    @return string             Description of success
210     *                               or failure.
211     *    @access public
212     */
213    function testMessage($compare) {
214        $dumper = &$this->_getDumper();
215        return 'Expected false, got [' . $dumper->describeValue($compare) . ']';
216    }
217}
218
219/**
220 *    Test for equality.
221 *    @package SimpleTest
222 *    @subpackage UnitTester
223 */
224class EqualExpectation extends SimpleExpectation {
225    var $_value;
226
227    /**
228     *    Sets the value to compare against.
229     *    @param mixed $value        Test value to match.
230     *    @param string $message     Customised message on failure.
231     *    @access public
232     */
233    function EqualExpectation($value, $message = '%s') {
234        $this->SimpleExpectation($message);
235        $this->_value = $value;
236    }
237
238    /**
239     *    Tests the expectation. True if it matches the
240     *    held value.
241     *    @param mixed $compare        Comparison value.
242     *    @return boolean              True if correct.
243     *    @access public
244     */
245    function test($compare) {
246        return (($this->_value == $compare) && ($compare == $this->_value));
247    }
248
249    /**
250     *    Returns a human readable test message.
251     *    @param mixed $compare      Comparison value.
252     *    @return string             Description of success
253     *                               or failure.
254     *    @access public
255     */
256    function testMessage($compare) {
257        if ($this->test($compare)) {
258            return "Equal expectation [" . $this->_dumper->describeValue($this->_value) . "]";
259        } else {
260            return "Equal expectation fails " .
261                    $this->_dumper->describeDifference($this->_value, $compare);
262        }
263    }
264
265    /**
266     *    Accessor for comparison value.
267     *    @return mixed       Held value to compare with.
268     *    @access protected
269     */
270    function _getValue() {
271        return $this->_value;
272    }
273}
274
275/**
276 *    Test for inequality.
277 *    @package SimpleTest
278 *    @subpackage UnitTester
279 */
280class NotEqualExpectation extends EqualExpectation {
281
282    /**
283     *    Sets the value to compare against.
284     *    @param mixed $value       Test value to match.
285     *    @param string $message    Customised message on failure.
286     *    @access public
287     */
288    function NotEqualExpectation($value, $message = '%s') {
289        $this->EqualExpectation($value, $message);
290    }
291
292    /**
293     *    Tests the expectation. True if it differs from the
294     *    held value.
295     *    @param mixed $compare        Comparison value.
296     *    @return boolean              True if correct.
297     *    @access public
298     */
299    function test($compare) {
300        return ! parent::test($compare);
301    }
302
303    /**
304     *    Returns a human readable test message.
305     *    @param mixed $compare      Comparison value.
306     *    @return string             Description of success
307     *                               or failure.
308     *    @access public
309     */
310    function testMessage($compare) {
311        $dumper = &$this->_getDumper();
312        if ($this->test($compare)) {
313            return "Not equal expectation passes " .
314                    $dumper->describeDifference($this->_getValue(), $compare);
315        } else {
316            return "Not equal expectation fails [" .
317                    $dumper->describeValue($this->_getValue()) .
318                    "] matches";
319        }
320    }
321}
322
323/**
324 *    Test for being within a range.
325 *    @package SimpleTest
326 *    @subpackage UnitTester
327 */
328class WithinMarginExpectation extends SimpleExpectation {
329    var $_upper;
330    var $_lower;
331
332    /**
333     *    Sets the value to compare against and the fuzziness of
334     *    the match. Used for comparing floating point values.
335     *    @param mixed $value        Test value to match.
336     *    @param mixed $margin       Fuzziness of match.
337     *    @param string $message     Customised message on failure.
338     *    @access public
339     */
340    function WithinMarginExpectation($value, $margin, $message = '%s') {
341        $this->SimpleExpectation($message);
342        $this->_upper = $value + $margin;
343        $this->_lower = $value - $margin;
344    }
345
346    /**
347     *    Tests the expectation. True if it matches the
348     *    held value.
349     *    @param mixed $compare        Comparison value.
350     *    @return boolean              True if correct.
351     *    @access public
352     */
353    function test($compare) {
354        return (($compare <= $this->_upper) && ($compare >= $this->_lower));
355    }
356
357    /**
358     *    Returns a human readable test message.
359     *    @param mixed $compare      Comparison value.
360     *    @return string             Description of success
361     *                               or failure.
362     *    @access public
363     */
364    function testMessage($compare) {
365        if ($this->test($compare)) {
366            return $this->_withinMessage($compare);
367        } else {
368            return $this->_outsideMessage($compare);
369        }
370    }
371
372    /**
373     *    Creates a the message for being within the range.
374     *    @param mixed $compare        Value being tested.
375     *    @access private
376     */
377    function _withinMessage($compare) {
378        return "Within expectation [" . $this->_dumper->describeValue($this->_lower) . "] and [" .
379                $this->_dumper->describeValue($this->_upper) . "]";
380    }
381
382    /**
383     *    Creates a the message for being within the range.
384     *    @param mixed $compare        Value being tested.
385     *    @access private
386     */
387    function _outsideMessage($compare) {
388        if ($compare > $this->_upper) {
389            return "Outside expectation " .
390                    $this->_dumper->describeDifference($compare, $this->_upper);
391        } else {
392            return "Outside expectation " .
393                    $this->_dumper->describeDifference($compare, $this->_lower);
394        }
395    }
396}
397
398/**
399 *    Test for being outside of a range.
400 *    @package SimpleTest
401 *    @subpackage UnitTester
402 */
403class OutsideMarginExpectation extends WithinMarginExpectation {
404
405    /**
406     *    Sets the value to compare against and the fuzziness of
407     *    the match. Used for comparing floating point values.
408     *    @param mixed $value        Test value to not match.
409     *    @param mixed $margin       Fuzziness of match.
410     *    @param string $message     Customised message on failure.
411     *    @access public
412     */
413    function OutsideMarginExpectation($value, $margin, $message = '%s') {
414        $this->WithinMarginExpectation($value, $margin, $message);
415    }
416
417    /**
418     *    Tests the expectation. True if it matches the
419     *    held value.
420     *    @param mixed $compare        Comparison value.
421     *    @return boolean              True if correct.
422     *    @access public
423     */
424    function test($compare) {
425        return ! parent::test($compare);
426    }
427
428    /**
429     *    Returns a human readable test message.
430     *    @param mixed $compare      Comparison value.
431     *    @return string             Description of success
432     *                               or failure.
433     *    @access public
434     */
435    function testMessage($compare) {
436        if (! $this->test($compare)) {
437            return $this->_withinMessage($compare);
438        } else {
439            return $this->_outsideMessage($compare);
440        }
441    }
442}
443
444/**
445 *    Test for reference.
446 *    @package SimpleTest
447 *    @subpackage UnitTester
448 */
449class ReferenceExpectation extends SimpleExpectation {
450    var $_value;
451
452    /**
453     *    Sets the reference value to compare against.
454     *    @param mixed $value       Test reference to match.
455     *    @param string $message    Customised message on failure.
456     *    @access public
457     */
458    function ReferenceExpectation(&$value, $message = '%s') {
459        $this->SimpleExpectation($message);
460        $this->_value =& $value;
461    }
462
463    /**
464     *    Tests the expectation. True if it exactly
465     *    references the held value.
466     *    @param mixed $compare        Comparison reference.
467     *    @return boolean              True if correct.
468     *    @access public
469     */
470    function test(&$compare) {
471        return SimpleTestCompatibility::isReference($this->_value, $compare);
472    }
473
474    /**
475     *    Returns a human readable test message.
476     *    @param mixed $compare      Comparison value.
477     *    @return string             Description of success
478     *                               or failure.
479     *    @access public
480     */
481    function testMessage($compare) {
482        if ($this->test($compare)) {
483            return "Reference expectation [" . $this->_dumper->describeValue($this->_value) . "]";
484        } else {
485            return "Reference expectation fails " .
486                    $this->_dumper->describeDifference($this->_value, $compare);
487        }
488    }
489
490    function _getValue() {
491        return $this->_value;
492    }
493}
494
495/**
496 *    Test for identity.
497 *    @package SimpleTest
498 *    @subpackage UnitTester
499 */
500class IdenticalExpectation extends EqualExpectation {
501
502    /**
503     *    Sets the value to compare against.
504     *    @param mixed $value       Test value to match.
505     *    @param string $message    Customised message on failure.
506     *    @access public
507     */
508    function IdenticalExpectation($value, $message = '%s') {
509        $this->EqualExpectation($value, $message);
510    }
511
512    /**
513     *    Tests the expectation. True if it exactly
514     *    matches the held value.
515     *    @param mixed $compare        Comparison value.
516     *    @return boolean              True if correct.
517     *    @access public
518     */
519    function test($compare) {
520        return SimpleTestCompatibility::isIdentical($this->_getValue(), $compare);
521    }
522
523    /**
524     *    Returns a human readable test message.
525     *    @param mixed $compare      Comparison value.
526     *    @return string             Description of success
527     *                               or failure.
528     *    @access public
529     */
530    function testMessage($compare) {
531        $dumper = &$this->_getDumper();
532        if ($this->test($compare)) {
533            return "Identical expectation [" . $dumper->describeValue($this->_getValue()) . "]";
534        } else {
535            return "Identical expectation [" . $dumper->describeValue($this->_getValue()) .
536                    "] fails with [" .
537                    $dumper->describeValue($compare) . "] " .
538                    $dumper->describeDifference($this->_getValue(), $compare, TYPE_MATTERS);
539        }
540    }
541}
542
543/**
544 *    Test for non-identity.
545 *    @package SimpleTest
546 *    @subpackage UnitTester
547 */
548class NotIdenticalExpectation extends IdenticalExpectation {
549
550    /**
551     *    Sets the value to compare against.
552     *    @param mixed $value        Test value to match.
553     *    @param string $message     Customised message on failure.
554     *    @access public
555     */
556    function NotIdenticalExpectation($value, $message = '%s') {
557        $this->IdenticalExpectation($value, $message);
558    }
559
560    /**
561     *    Tests the expectation. True if it differs from the
562     *    held value.
563     *    @param mixed $compare        Comparison value.
564     *    @return boolean              True if correct.
565     *    @access public
566     */
567    function test($compare) {
568        return ! parent::test($compare);
569    }
570
571    /**
572     *    Returns a human readable test message.
573     *    @param mixed $compare      Comparison value.
574     *    @return string             Description of success
575     *                               or failure.
576     *    @access public
577     */
578    function testMessage($compare) {
579        $dumper = &$this->_getDumper();
580        if ($this->test($compare)) {
581            return "Not identical expectation passes " .
582                    $dumper->describeDifference($this->_getValue(), $compare, TYPE_MATTERS);
583        } else {
584            return "Not identical expectation [" . $dumper->describeValue($this->_getValue()) . "] matches";
585        }
586    }
587}
588
589/**
590 *    Test for a pattern using Perl regex rules.
591 *    @package SimpleTest
592 *    @subpackage UnitTester
593 */
594class PatternExpectation extends SimpleExpectation {
595    var $_pattern;
596
597    /**
598     *    Sets the value to compare against.
599     *    @param string $pattern    Pattern to search for.
600     *    @param string $message    Customised message on failure.
601     *    @access public
602     */
603    function PatternExpectation($pattern, $message = '%s') {
604        $this->SimpleExpectation($message);
605        $this->_pattern = $pattern;
606    }
607
608    /**
609     *    Accessor for the pattern.
610     *    @return string       Perl regex as string.
611     *    @access protected
612     */
613    function _getPattern() {
614        return $this->_pattern;
615    }
616
617    /**
618     *    Tests the expectation. True if the Perl regex
619     *    matches the comparison value.
620     *    @param string $compare        Comparison value.
621     *    @return boolean               True if correct.
622     *    @access public
623     */
624    function test($compare) {
625        return (boolean)preg_match($this->_getPattern(), $compare);
626    }
627
628    /**
629     *    Returns a human readable test message.
630     *    @param mixed $compare      Comparison value.
631     *    @return string             Description of success
632     *                               or failure.
633     *    @access public
634     */
635    function testMessage($compare) {
636        if ($this->test($compare)) {
637            return $this->_describePatternMatch($this->_getPattern(), $compare);
638        } else {
639            $dumper = &$this->_getDumper();
640            return "Pattern [" . $this->_getPattern() .
641                    "] not detected in [" .
642                    $dumper->describeValue($compare) . "]";
643        }
644    }
645
646    /**
647     *    Describes a pattern match including the string
648     *    found and it's position.
649     *    @param string $pattern        Regex to match against.
650     *    @param string $subject        Subject to search.
651     *    @access protected
652     */
653    function _describePatternMatch($pattern, $subject) {
654        preg_match($pattern, $subject, $matches);
655        $position = strpos($subject, $matches[0]);
656        $dumper = $this->_getDumper();
657        return "Pattern [$pattern] detected at character [$position] in [" .
658                $dumper->describeValue($subject) . "] as [" .
659                $matches[0] . "] in region [" .
660                $dumper->clipString($subject, 100, $position) . "]";
661    }
662}
663
664/**
665 *    @package SimpleTest
666 *    @subpackage UnitTester
667 *    @deprecated
668 */
669class WantedPatternExpectation extends PatternExpectation {
670}
671
672/**
673 *    Fail if a pattern is detected within the
674 *    comparison.
675 *    @package SimpleTest
676 *    @subpackage UnitTester
677 */
678class NoPatternExpectation extends PatternExpectation {
679
680    /**
681     *    Sets the reject pattern
682     *    @param string $pattern    Pattern to search for.
683     *    @param string $message    Customised message on failure.
684     *    @access public
685     */
686    function NoPatternExpectation($pattern, $message = '%s') {
687        $this->PatternExpectation($pattern, $message);
688    }
689
690    /**
691     *    Tests the expectation. False if the Perl regex
692     *    matches the comparison value.
693     *    @param string $compare        Comparison value.
694     *    @return boolean               True if correct.
695     *    @access public
696     */
697    function test($compare) {
698        return ! parent::test($compare);
699    }
700
701    /**
702     *    Returns a human readable test message.
703     *    @param string $compare      Comparison value.
704     *    @return string              Description of success
705     *                                or failure.
706     *    @access public
707     */
708    function testMessage($compare) {
709        if ($this->test($compare)) {
710            $dumper = &$this->_getDumper();
711            return "Pattern [" . $this->_getPattern() .
712                    "] not detected in [" .
713                    $dumper->describeValue($compare) . "]";
714        } else {
715            return $this->_describePatternMatch($this->_getPattern(), $compare);
716        }
717    }
718}
719
720/**
721 *    @package SimpleTest
722 *    @subpackage UnitTester
723 *      @deprecated
724 */
725class UnwantedPatternExpectation extends NoPatternExpectation {
726}
727
728/**
729 *    Tests either type or class name if it's an object.
730 *      @package SimpleTest
731 *      @subpackage UnitTester
732 */
733class IsAExpectation extends SimpleExpectation {
734    var $_type;
735
736    /**
737     *    Sets the type to compare with.
738     *    @param string $type       Type or class name.
739     *    @param string $message    Customised message on failure.
740     *    @access public
741     */
742    function IsAExpectation($type, $message = '%s') {
743        $this->SimpleExpectation($message);
744        $this->_type = $type;
745    }
746
747    /**
748     *    Accessor for type to check against.
749     *    @return string    Type or class name.
750     *    @access protected
751     */
752    function _getType() {
753        return $this->_type;
754    }
755
756    /**
757     *    Tests the expectation. True if the type or
758     *    class matches the string value.
759     *    @param string $compare        Comparison value.
760     *    @return boolean               True if correct.
761     *    @access public
762     */
763    function test($compare) {
764        if (is_object($compare)) {
765            return SimpleTestCompatibility::isA($compare, $this->_type);
766        } else {
767            return (strtolower(gettype($compare)) == $this->_canonicalType($this->_type));
768        }
769    }
770
771    /**
772     *    Coerces type name into a gettype() match.
773     *    @param string $type        User type.
774     *    @return string             Simpler type.
775     *    @access private
776     */
777    function _canonicalType($type) {
778        $type = strtolower($type);
779        $map = array(
780                'bool' => 'boolean',
781                'float' => 'double',
782                'real' => 'double',
783                'int' => 'integer');
784        if (isset($map[$type])) {
785            $type = $map[$type];
786        }
787        return $type;
788    }
789
790    /**
791     *    Returns a human readable test message.
792     *    @param mixed $compare      Comparison value.
793     *    @return string             Description of success
794     *                               or failure.
795     *    @access public
796     */
797    function testMessage($compare) {
798        $dumper = &$this->_getDumper();
799        return "Value [" . $dumper->describeValue($compare) .
800                "] should be type [" . $this->_type . "]";
801    }
802}
803
804/**
805 *    Tests either type or class name if it's an object.
806 *    Will succeed if the type does not match.
807 *      @package SimpleTest
808 *      @subpackage UnitTester
809 */
810class NotAExpectation extends IsAExpectation {
811    var $_type;
812
813    /**
814     *    Sets the type to compare with.
815     *    @param string $type       Type or class name.
816     *    @param string $message    Customised message on failure.
817     *    @access public
818     */
819    function NotAExpectation($type, $message = '%s') {
820        $this->IsAExpectation($type, $message);
821    }
822
823    /**
824     *    Tests the expectation. False if the type or
825     *    class matches the string value.
826     *    @param string $compare        Comparison value.
827     *    @return boolean               True if different.
828     *    @access public
829     */
830    function test($compare) {
831        return ! parent::test($compare);
832    }
833
834    /**
835     *    Returns a human readable test message.
836     *    @param mixed $compare      Comparison value.
837     *    @return string             Description of success
838     *                               or failure.
839     *    @access public
840     */
841    function testMessage($compare) {
842        $dumper = &$this->_getDumper();
843        return "Value [" . $dumper->describeValue($compare) .
844                "] should not be type [" . $this->_getType() . "]";
845    }
846}
847
848/**
849 *    Tests for existance of a method in an object
850 *      @package SimpleTest
851 *      @subpackage UnitTester
852 */
853class MethodExistsExpectation extends SimpleExpectation {
854    var $_method;
855
856    /**
857     *    Sets the value to compare against.
858     *    @param string $method     Method to check.
859     *    @param string $message    Customised message on failure.
860     *    @access public
861     *    @return void
862     */
863    function MethodExistsExpectation($method, $message = '%s') {
864        $this->SimpleExpectation($message);
865        $this->_method = &$method;
866    }
867
868    /**
869     *    Tests the expectation. True if the method exists in the test object.
870     *    @param string $compare        Comparison method name.
871     *    @return boolean               True if correct.
872     *    @access public
873     */
874    function test($compare) {
875        return (boolean)(is_object($compare) && method_exists($compare, $this->_method));
876    }
877
878    /**
879     *    Returns a human readable test message.
880     *    @param mixed $compare      Comparison value.
881     *    @return string             Description of success
882     *                               or failure.
883     *    @access public
884     */
885    function testMessage($compare) {
886        $dumper = &$this->_getDumper();
887        if (! is_object($compare)) {
888            return 'No method on non-object [' . $dumper->describeValue($compare) . ']';
889        }
890        $method = $this->_method;
891        return "Object [" . $dumper->describeValue($compare) .
892                "] should contain method [$method]";
893    }
894}
895?>
Note: See TracBrowser for help on using the repository browser.