Repository: buildbot/buildbot

Issue:
Initial creation of logentries status plugin
This is a first attempt at creating a logentries status plugin.

This takes the build started and finished events and just sends them to logentries. 



Discussion:

Resolving patch:
diff --git a/master/buildbot/process/properties.py b/master/buildbot/process/properties.py
--- a/master/buildbot/process/properties.py
+++ b/master/buildbot/process/properties.py
@@ -272,7 +272,7 @@ class WithProperties(util.ComparableMixin):
     """
 
     implements(IRenderable)
-    compare_attrs = ('fmtstring', 'args')
+    compare_attrs = ('fmtstring', 'args', 'lambda_subs')
 
     def __init__(self, fmtstring, *args, **lambda_subs):
         self.fmtstring = fmtstring
@@ -301,9 +301,11 @@ def getRenderingFor(self, build):
 
 
 _notHasKey = object() ## Marker object for _Lookup(..., hasKey=...) default
-class _Lookup(util.ComparableMixin):
+class _Lookup(util.ComparableMixin, object):
     implements(IRenderable)
 
+    compare_attrs = ('value', 'index', 'default', 'defaultWhenFalse', 'hasKey', 'elideNoneAs')
+
     def __init__(self, value, index, default=None,
             defaultWhenFalse=True, hasKey=_notHasKey,
             elideNoneAs=None):
@@ -314,6 +316,20 @@ def __init__(self, value, index, default=None,
         self.hasKey = hasKey
         self.elideNoneAs = elideNoneAs
 
+    def __repr__(self):
+        return '_Lookup(%r, %r%s%s%s%s)' % (
+                self.value,
+                self.index,
+                ', default=%r' % (self.default,)
+                    if self.default is not None else '',
+                ', defaultWhenFalse=False'
+                    if not self.defaultWhenFalse else '',
+                ', hasKey=%r' % (self.hasKey,)
+                    if self.hasKey is not _notHasKey else '',
+                ', elideNoneAs=%r'% (self.elideNoneAs,)
+                    if self.elideNoneAs is not None else '')
+
+
     @defer.inlineCallbacks
     def getRenderingFor(self, build):
         value = build.render(self.value)
@@ -350,8 +366,11 @@ def getRenderingFor(self, build):
         return build.getProperties()
 _thePropertyDict = _PropertyDict()
 
-class _SourceStampDict(object):
+class _SourceStampDict(util.ComparableMixin, object):
     implements(IRenderable)
+
+    compare_attrs = ('codebase',)
+
     def __init__(self, codebase):
         self.codebase = codebase
     def getRenderingFor(self, build):
@@ -361,16 +380,20 @@ def getRenderingFor(self, build):
         else:
             return {}
 
-
-class _Lazy(object):
+class _Lazy(util.ComparableMixin, object):
     implements(IRenderable)
+
+    compare_attrs = ('value',)
     def __init__(self, value):
         self.value = value
     def getRenderingFor(self, build):
         return self.value
+
+    def __repr__(self):
+        return '_Lazy(%r)' % self.value
  
 
-class Interpolate(util.ComparableMixin): 
+class Interpolate(util.ComparableMixin, object):
     """ 
     This is a marker class, used fairly widely to indicate that we 
     want to interpolate build properties. 
@@ -392,6 +415,12 @@ def __init__(self, fmtstring, *args, **kwargs):
             self.interpolations = {}
             self._parse(fmtstring)
 
+    def __repr__(self):
+        if self.args:
+            return 'Interpolate(%r, *%r)' % (self.fmtstring, self.args)
+        if self.kwargs:
+            return 'Interpolate(%r, **%r)' % (self.fmtstring, self.kwargs)
+
     @staticmethod
     def _parse_prop(arg):
         try:
@@ -570,11 +599,17 @@ def checkDefault(rv):
             else:
                 return props.render(self.default)
 
-class _Renderer(object):
+class _Renderer(util.ComparableMixin, object):
     implements(IRenderable)
+
+    compare_attrs = ('getRenderingFor',)
+
     def __init__(self, fn):
         self.getRenderingFor = fn
 
+    def __repr__(self):
+        return 'renderer(%r)' % (self.getRenderingFor,)
+
 def renderer(fn):
     return _Renderer(fn)
 


Test patch:
diff --git a/master/buildbot/test/unit/test_process_properties.py b/master/buildbot/test/unit/test_process_properties.py
--- a/master/buildbot/test/unit/test_process_properties.py
+++ b/master/buildbot/test/unit/test_process_properties.py
@@ -20,6 +20,7 @@
 from twisted.python import components
 from buildbot.process.properties import Properties, WithProperties
 from buildbot.process.properties import Interpolate
+from buildbot.process.properties import _Lazy, _SourceStampDict, _Lookup
 from buildbot.process.properties import Property, PropertiesMixin, renderer
 from buildbot.interfaces import IRenderable, IProperties
 from buildbot.test.util.config import ConfigErrorsMixin
@@ -1275,3 +1276,134 @@ def test_renderer_fails(self):
         self.failUnlessFailure(d, RuntimeError)
         return d
 
+class Compare(unittest.TestCase):
+
+    def test_WithProperties_lambda(self):
+        self.failIfEqual(WithProperties("%(key)s", key=lambda p:'val'), WithProperties("%(key)s", key=lambda p:'val'))
+        def rend(p):
+            return "val"
+        self.failUnlessEqual(
+                WithProperties("%(key)s", key=rend),
+                WithProperties("%(key)s", key=rend))
+        self.failIfEqual(
+                WithProperties("%(key)s", key=rend),
+                WithProperties("%(key)s", otherkey=rend))
+
+    def test_WithProperties_positional(self):
+        self.failIfEqual(
+                WithProperties("%s", 'key'),
+                WithProperties("%s", 'otherkey'))
+        self.failUnlessEqual(
+                WithProperties("%s", 'key'),
+                WithProperties("%s", 'key'))
+        self.failIfEqual(
+                WithProperties("%s", 'key'),
+                WithProperties("k%s", 'key'))
+
+    def test_Interpolate_constant(self):
+        self.failIfEqual(
+                Interpolate('some text here'),
+                Interpolate('and other text there'))
+        self.failUnlessEqual(
+                Interpolate('some text here'),
+                Interpolate('some text here'))
+
+    def test_Interpolate_positional(self):
+        self.failIfEqual(
+                Interpolate('%s %s', "test", "text"),
+                Interpolate('%s %s', "other", "text"))
+        self.failUnlessEqual(
+                Interpolate('%s %s', "test", "text"),
+                Interpolate('%s %s', "test", "text"))
+
+    def test_Interpolate_kwarg(self):
+        self.failIfEqual(
+                Interpolate("%(kw:test)s", test=object(), other=2),
+                Interpolate("%(kw:test)s", test=object(), other=2))
+        self.failUnlessEqual(
+                Interpolate('testing: %(kw:test)s', test="test", other=3),
+                Interpolate('testing: %(kw:test)s', test="test", other=3))
+
+    def test_renderer(self):
+        self.failIfEqual(
+                renderer(lambda p:'val'),
+                renderer(lambda p:'val'))
+        def rend(p):
+            return "val"
+        self.failUnlessEqual(
+                renderer(rend),
+                renderer(rend))
+
+    def test_Lookup_simple(self):
+        self.failIfEqual(
+                _Lookup({'test': 5, 'other': 6}, 'other'),
+                _Lookup({'test': 5, 'other': 6}, 'test'))
+        self.failUnlessEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test'),
+                _Lookup({'test': 5, 'other': 6}, 'test'))
+
+    def test_Lookup_default(self):
+        self.failIfEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', default='default'),
+                _Lookup({'test': 5, 'other': 6}, 'test'))
+        self.failUnlessEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', default='default'),
+                _Lookup({'test': 5, 'other': 6}, 'test', default='default'))
+
+    def test_Lookup_defaultWhenFalse(self):
+        self.failIfEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', defaultWhenFalse=False),
+                _Lookup({'test': 5, 'other': 6}, 'test'))
+        self.failIfEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', defaultWhenFalse=False),
+                _Lookup({'test': 5, 'other': 6}, 'test', defaultWhenFalse=True))
+        self.failUnlessEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', defaultWhenFalse=True),
+                _Lookup({'test': 5, 'other': 6}, 'test', defaultWhenFalse=True))
+        self.failUnlessEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test'),
+                _Lookup({'test': 5, 'other': 6}, 'test', defaultWhenFalse=True))
+
+    def test_Lookup_hasKey(self):
+        self.failIfEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', hasKey=None),
+                _Lookup({'test': 5, 'other': 6}, 'test'))
+        self.failIfEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', hasKey='has-key'),
+                _Lookup({'test': 5, 'other': 6}, 'test'))
+        self.failIfEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', hasKey='has-key'),
+                _Lookup({'test': 5, 'other': 6}, 'test', hasKey='other-key'))
+        self.failUnlessEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', hasKey='has-key'),
+                _Lookup({'test': 5, 'other': 6}, 'test', hasKey='has-key'))
+
+    def test_Lookup_elideNoneAs(self):
+        self.failUnlessEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', elideNoneAs=None),
+                _Lookup({'test': 5, 'other': 6}, 'test'))
+        self.failIfEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', elideNoneAs=''),
+                _Lookup({'test': 5, 'other': 6}, 'test'))
+        self.failIfEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', elideNoneAs='got None'),
+                _Lookup({'test': 5, 'other': 6}, 'test', elideNoneAs=''))
+        self.failUnlessEqual(
+                _Lookup({'test': 5, 'other': 6}, 'test', elideNoneAs='got None'),
+                _Lookup({'test': 5, 'other': 6}, 'test', elideNoneAs='got None'))
+
+    def test_Lazy(self):
+        self.failIfEqual(
+                _Lazy(5),
+                _Lazy(6))
+        self.failUnlessEqual(
+                _Lazy(5),
+                _Lazy(5))
+
+    def test_SourceStampDict(self):
+        self.failIfEqual(
+                _SourceStampDict('binary'),
+                _SourceStampDict('library'))
+        self.failUnlessEqual(
+                _SourceStampDict('binary'),
+                _SourceStampDict('binary'))