--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/tests/test-check-interfaces.py Thu Aug 10 21:00:30 2017 -0700
@@ -0,0 +1,71 @@
+# Test that certain objects conform to well-defined interfaces.
+
+from __future__ import absolute_import, print_function
+
+from mercurial import (
+ httppeer,
+ localrepo,
+ sshpeer,
+ ui as uimod,
+)
+
+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
+
+# Facilitates testing sshpeer without requiring an SSH server.
+class testingsshpeer(sshpeer.sshpeer):
+ def _validaterepo(self, *args, **kwargs):
+ pass
+
+class badpeer(httppeer.httppeer):
+ def __init__(self):
+ super(badpeer, self).__init__(uimod.ui(), 'http://localhost')
+ self.badattribute = True
+
+ def badmethod(self):
+ pass
+
+def main():
+ ui = uimod.ui()
+
+ checkobject(badpeer())
+ checkobject(httppeer.httppeer(ui, 'http://localhost'))
+ checkobject(localrepo.localpeer(dummyrepo()))
+ checkobject(testingsshpeer(ui, 'ssh://localhost/foo'))
+
+main()