tests/test-check-interfaces.py
author Gregory Szorc <gregory.szorc@gmail.com>
Wed, 07 Mar 2018 20:41:59 -0800
changeset 37006 8e89c2bec1f7
parent 36370 11ba1a96f946
child 37181 0dfb5672f015
permissions -rw-r--r--
httppeer: refactor how httppeer is created (API) Previously, we passed a bunch of arguments to httppeer.__init__, validated them, then possibly constructed a valid instance. A short while ago, we refactored sshpeer so all the validation and setup work occurs before the constructor. We introduced a makepeer() to hold most of this logic. This commit gives httppeer the same treatment. As a sign that the previous design was poor, __del__ no longer conditionally checks for the presence of an attribute that may not be defined (it is always defined in the new code). .. api:: httppeer.httppeer.__init__ now takes additional arguments. Instances should be obtained by calling httppeer.instance() or httppeer.makepeer() instead. Differential Revision: https://phab.mercurial-scm.org/D2725

# Test that certain objects conform to well-defined interfaces.

from __future__ import absolute_import, print_function

from mercurial import (
    bundlerepo,
    httppeer,
    localrepo,
    sshpeer,
    statichttprepo,
    ui as uimod,
    unionrepo,
)

def checkobject(o):
    """Verify a constructed object conforms to interface rules.

    An object must have __abstractmethods__ defined.

    All "public" attributes of the object (attributes not prefixed with
    an underscore) must be in __abstractmethods__ or appear on a base class
    with __abstractmethods__.
    """
    name = o.__class__.__name__

    allowed = set()
    for cls in o.__class__.__mro__:
        if not getattr(cls, '__abstractmethods__', set()):
            continue

        allowed |= cls.__abstractmethods__
        allowed |= {a for a in dir(cls) if not a.startswith('_')}

    if not allowed:
        print('%s does not have abstract methods' % name)
        return

    public = {a for a in dir(o) if not a.startswith('_')}

    for attr in sorted(public - allowed):
        print('public attributes not in abstract interface: %s.%s' % (
            name, attr))

# Facilitates testing localpeer.
class dummyrepo(object):
    def __init__(self):
        self.ui = uimod.ui()
    def filtered(self, name):
        pass
    def _restrictcapabilities(self, caps):
        pass

class dummyopener(object):
    handlers = []

# Facilitates testing sshpeer without requiring an SSH server.
class badpeer(httppeer.httppeer):
    def __init__(self):
        super(badpeer, self).__init__(None, None, None, dummyopener())
        self.badattribute = True

    def badmethod(self):
        pass

class dummypipe(object):
    def close(self):
        pass

def main():
    ui = uimod.ui()

    checkobject(badpeer())
    checkobject(httppeer.httppeer(None, None, None, dummyopener()))
    checkobject(localrepo.localpeer(dummyrepo()))
    checkobject(sshpeer.sshv1peer(ui, 'ssh://localhost/foo', None, dummypipe(),
                                  dummypipe(), None, None))
    checkobject(sshpeer.sshv2peer(ui, 'ssh://localhost/foo', None, dummypipe(),
                                  dummypipe(), None, None))
    checkobject(bundlerepo.bundlepeer(dummyrepo()))
    checkobject(statichttprepo.statichttppeer(dummyrepo()))
    checkobject(unionrepo.unionpeer(dummyrepo()))

main()