~testtools-dev/testtools/trunk

« back to all changes in this revision

Viewing changes to testtools/matchers.py

  • Committer: Jonathan Lange
  • Date: 2011-08-15 13:24:05 UTC
  • mfrom: (225.1.11 matchers)
  • Revision ID: jml@canonical.com-20110815132405-stayl9hjlxf3ttga
 * Migrate assert* methods to use matchers
 * Add optional message parameter to assertThat

Show diffs side-by-side

added added

removed removed

Lines of Context:
15
15
    'AfterPreprocessing',
16
16
    'AllMatch',
17
17
    'Annotate',
 
18
    'Contains',
18
19
    'DocTestMatches',
19
20
    'EndsWith',
20
21
    'Equals',
21
22
    'GreaterThan',
22
23
    'Is',
 
24
    'IsInstance',
23
25
    'KeysEqual',
24
26
    'LessThan',
25
27
    'MatchesAll',
242
244
        return self.matcher._describe_difference(self.with_nl)
243
245
 
244
246
 
 
247
class DoesNotContain(Mismatch):
 
248
 
 
249
    def __init__(self, matchee, needle):
 
250
        """Create a DoesNotContain Mismatch.
 
251
 
 
252
        :param matchee: the object that did not contain needle.
 
253
        :param needle: the needle that 'matchee' was expected to contain.
 
254
        """
 
255
        self.matchee = matchee
 
256
        self.needle = needle
 
257
 
 
258
    def describe(self):
 
259
        return "%r not in %r" % (self.needle, self.matchee)
 
260
 
 
261
 
245
262
class DoesNotStartWith(Mismatch):
246
263
 
247
264
    def __init__(self, matchee, expected):
343
360
    mismatch_string = 'is not'
344
361
 
345
362
 
 
363
class IsInstance(object):
 
364
    """Matcher that wraps isinstance."""
 
365
 
 
366
    def __init__(self, *types):
 
367
        self.types = tuple(types)
 
368
 
 
369
    def __str__(self):
 
370
        return "%s(%s)" % (self.__class__.__name__,
 
371
                ', '.join(type.__name__ for type in self.types))
 
372
 
 
373
    def match(self, other):
 
374
        if isinstance(other, self.types):
 
375
            return None
 
376
        return NotAnInstance(other, self.types)
 
377
 
 
378
 
 
379
class NotAnInstance(Mismatch):
 
380
 
 
381
    def __init__(self, matchee, types):
 
382
        """Create a NotAnInstance Mismatch.
 
383
 
 
384
        :param matchee: the thing which is not an instance of any of types.
 
385
        :param types: A tuple of the types which were expected.
 
386
        """
 
387
        self.matchee = matchee
 
388
        self.types = types
 
389
 
 
390
    def describe(self):
 
391
        if len(self.types) == 1:
 
392
            typestr = self.types[0].__name__
 
393
        else:
 
394
            typestr = 'any of (%s)' % ', '.join(type.__name__ for type in
 
395
                    self.types)
 
396
        return "'%s' is not an instance of %s" % (self.matchee, typestr)
 
397
 
 
398
 
346
399
class LessThan(_BinaryComparison):
347
400
    """Matches if the item is less than the matchers reference object."""
348
401
 
449
502
        :param exception: Either an exception instance or type.
450
503
            If an instance is given, the type and arguments of the exception
451
504
            are checked. If a type is given only the type of the exception is
452
 
            checked.
 
505
            checked. If a tuple is given, then as with isinstance, any of the
 
506
            types in the tuple matching is sufficient to match.
453
507
        :param value_re: If 'exception' is a type, and the matchee exception
454
508
            is of the right type, then match against this.  If value_re is a
455
509
            string, then assume value_re is a regular expression and match
461
515
        if istext(value_re):
462
516
            value_re = AfterPreproccessing(str, MatchesRegex(value_re), False)
463
517
        self.value_re = value_re
464
 
        self._is_instance = type(self.expected) not in classtypes()
 
518
        self._is_instance = type(self.expected) not in classtypes() + (tuple,)
465
519
 
466
520
    def match(self, other):
467
521
        if type(other) != tuple:
484
538
        return "MatchesException(%s)" % repr(self.expected)
485
539
 
486
540
 
 
541
class Contains(Matcher):
 
542
    """Checks whether something is container in another thing."""
 
543
 
 
544
    def __init__(self, needle):
 
545
        """Create a Contains Matcher.
 
546
 
 
547
        :param needle: the thing that needs to be contained by matchees.
 
548
        """
 
549
        self.needle = needle
 
550
 
 
551
    def __str__(self):
 
552
        return "Contains(%r)" % (self.needle,)
 
553
 
 
554
    def match(self, matchee):
 
555
        try:
 
556
            if self.needle not in matchee:
 
557
                return DoesNotContain(matchee, self.needle)
 
558
        except TypeError:
 
559
            # e.g. 1 in 2 will raise TypeError
 
560
            return DoesNotContain(matchee, self.needle)
 
561
        return None
 
562
 
 
563
 
487
564
class StartsWith(Matcher):
488
565
    """Checks whether one string starts with another."""
489
566
 
613
690
        # Catch all exceptions: Raises() should be able to match a
614
691
        # KeyboardInterrupt or SystemExit.
615
692
        except:
 
693
            exc_info = sys.exc_info()
616
694
            if self.exception_matcher:
617
 
                mismatch = self.exception_matcher.match(sys.exc_info())
 
695
                mismatch = self.exception_matcher.match(exc_info)
618
696
                if not mismatch:
 
697
                    del exc_info
619
698
                    return
620
699
            else:
621
700
                mismatch = None
622
701
            # The exception did not match, or no explicit matching logic was
623
702
            # performed. If the exception is a non-user exception (that is, not
624
703
            # a subclass of Exception on Python 2.5+) then propogate it.
625
 
            if isbaseexception(sys.exc_info()[1]):
 
704
            if isbaseexception(exc_info[1]):
 
705
                del exc_info
626
706
                raise
627
707
            return mismatch
628
708