Patchwork D339: tests: verify that peer instances only expose interface members

login
register
mail settings
Submitter phabricator
Date Aug. 15, 2017, 5:51 p.m.
Message ID <b3a5a40ebbacdfe5599532a063c0c755@localhost.localdomain>
Download mbox | patch
Permalink /patch/23031/
State Not Applicable
Headers show

Comments

phabricator - Aug. 15, 2017, 5:51 p.m.
This revision was automatically updated to reflect the committed changes.
Closed by commit rHGb70029f355a3: tests: verify that peer instances only expose interface members (authored by indygreg).

REPOSITORY
  rHG Mercurial

CHANGES SINCE LAST UPDATE
  https://phab.mercurial-scm.org/D339?vs=764&id=938

REVISION DETAIL
  https://phab.mercurial-scm.org/D339

AFFECTED FILES
  tests/test-check-interfaces.py
  tests/test-check-interfaces.py.out

CHANGE DETAILS




To: indygreg, #hg-reviewers, durin42
Cc: durin42, mercurial-devel

Patch

diff --git a/tests/test-check-interfaces.py.out b/tests/test-check-interfaces.py.out
new file mode 100644
--- /dev/null
+++ b/tests/test-check-interfaces.py.out
@@ -0,0 +1,2 @@ 
+public attributes not in abstract interface: badpeer.badattribute
+public attributes not in abstract interface: badpeer.badmethod
diff --git a/tests/test-check-interfaces.py b/tests/test-check-interfaces.py
new file mode 100644
--- /dev/null
+++ b/tests/test-check-interfaces.py
@@ -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()