[test/statsd] add tests for cubicweb.statsd_logger
authorDavid Douard <david.douard@logilab.fr>
Tue, 24 Apr 2018 15:21:18 +0200
changeset 12307 d507cbe169ab
parent 12306 c96dd92e480e
child 12308 cbbcfa69a0e7
[test/statsd] add tests for cubicweb.statsd_logger
cubicweb/statsd_logger.py
cubicweb/test/unittest_statsd.py
flake8-ok-files.txt
--- a/cubicweb/statsd_logger.py	Tue Apr 24 17:16:44 2018 +0200
+++ b/cubicweb/statsd_logger.py	Tue Apr 24 15:21:18 2018 +0200
@@ -88,19 +88,32 @@
     _socket = socket.socket(family, socket.SOCK_DGRAM)
 
 
+def teardown():
+    """Unconfigure the statsd endpoint
+
+    This is most likely only useful for unit tests"""
+    global _bucket, _address, _socket
+    _bucket = 'cubicweb'
+    _address = None
+    _socket = None
+
+
 def statsd_c(context, n=1):
     if _address is not None:
-        _socket.sendto('{0}.{1}:{2}|c\n'.format(_bucket, context, n), _address)
+        _socket.sendto('{0}.{1}:{2}|c\n'.format(_bucket, context, n).encode(),
+                       _address)
 
 
 def statsd_g(context, value):
     if _address is not None:
-        _socket.sendto('{0}.{1}:{2}|g\n'.format(_bucket, context, value), _address)
+        _socket.sendto('{0}.{1}:{2}|g\n'.format(_bucket, context, value).encode(),
+                       _address)
 
 
 def statsd_t(context, value):
     if _address is not None:
-        _socket.sendto('{0}.{1}:{2:.4f}|ms\n'.format(_bucket, context, value), _address)
+        _socket.sendto('{0}.{1}:{2:.4f}|ms\n'.format(_bucket, context, value).encode(),
+                       _address)
 
 
 class statsd_timeit(object):
@@ -126,7 +139,7 @@
         finally:
             dt = 1000 * (time.time() - t0)
             msg = '{0}.{1}:{2:.4f}|ms\n{0}.{1}:1|c\n'.format(
-                _bucket, self.__name__, dt)
+                _bucket, self.__name__, dt).encode()
             _socket.sendto(msg, _address)
 
     def __get__(self, obj, objtype):
@@ -147,5 +160,5 @@
         if _address is not None:
             dt = 1000 * (time.time() - t0)
             msg = '{0}.{1}:{2:.4f}|ms\n{0}.{1}:1|c\n'.format(
-                _bucket, ctxmsg, dt)
+                _bucket, ctxmsg, dt).encode()
             _socket.sendto(msg, _address)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/cubicweb/test/unittest_statsd.py	Tue Apr 24 15:21:18 2018 +0200
@@ -0,0 +1,136 @@
+# -*- coding: utf-8 -*-
+# copyright 2018 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
+#
+# This file is part of CubicWeb.
+#
+# CubicWeb is free software: you can redistribute it and/or modify it under the
+# terms of the GNU Lesser General Public License as published by the Free
+# Software Foundation, either version 2.1 of the License, or (at your option)
+# any later version.
+#
+# CubicWeb is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
+# details.
+#
+# You should have received a copy of the GNU Lesser General Public License along
+# with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
+"""unit tests for module cubicweb.statsd_logger"""
+
+import threading
+import socket
+import time
+import re
+
+from unittest import TestCase
+from cubicweb import statsd_logger as statsd
+
+
+UDP_PORT = None
+RUNNING = True
+SOCK = socket.socket(socket.AF_INET,
+                     socket.SOCK_DGRAM)
+SOCK.settimeout(0.1)
+STATSD = None
+DATA = []
+
+
+def statsd_rcv():
+    while RUNNING:
+        try:
+            data, addr = SOCK.recvfrom(1024)
+            if data:
+                rcv = [row.strip().decode() for row in data.splitlines()]
+                DATA.extend(rcv)
+        except socket.timeout:
+            pass
+
+
+def setUpModule(*args):
+    global UDP_PORT, STATSD
+    SOCK.bind(('127.0.0.1', 0))
+    UDP_PORT = SOCK.getsockname()[1]
+    STATSD = threading.Thread(target=statsd_rcv)
+    STATSD.start()
+    statsd.setup('test', ('127.0.0.1', UDP_PORT))
+
+
+def tearDownModule(*args):
+    global RUNNING
+    RUNNING = False
+    STATSD.join()
+    statsd.teardown()
+
+
+class StatsdTC(TestCase):
+
+    def setUp(self):
+        super(StatsdTC, self).setUp()
+        DATA[:] = []
+
+    def check_received(self, value):
+        for i in range(10):
+            if value in DATA:
+                break
+            time.sleep(0.01)
+        else:
+            self.assertIn(value, DATA)
+
+    def check_received_ms(self, value):
+        value = re.compile(value.replace('?', '\d'))
+        for i in range(10):
+            if [x for x in DATA if value.match(x)]:
+                break
+            time.sleep(0.01)
+        else:
+            self.assertTrue([x for x in DATA if value.match(x)], DATA)
+
+    def test_statsd_c(self):
+        statsd.statsd_c('context')
+        self.check_received('test.context:1|c')
+        statsd.statsd_c('context', 10)
+        self.check_received('test.context:10|c')
+
+    def test_statsd_g(self):
+        statsd.statsd_g('context', 42)
+        self.check_received('test.context:42|g')
+        statsd.statsd_g('context', 'Igorrr')
+        self.check_received('test.context:Igorrr|g')
+
+    def test_statsd_t(self):
+        statsd.statsd_t('context', 1)
+        self.check_received('test.context:1.0000|ms')
+        statsd.statsd_t('context', 10)
+        self.check_received('test.context:10.0000|ms')
+        statsd.statsd_t('context', 0.12344)
+        self.check_received('test.context:0.1234|ms')
+        statsd.statsd_t('context', 0.12345)
+        self.check_received('test.context:0.1235|ms')
+
+    def test_decorator(self):
+
+        @statsd.statsd_timeit
+        def measure_me_please():
+            "some nice function"
+            return 42
+
+        self.assertEqual(measure_me_please.__doc__,
+                         "some nice function")
+
+        measure_me_please()
+        self.check_received_ms('test.measure_me_please:0.0???|ms')
+        self.check_received('test.measure_me_please:1|c')
+
+    def test_context_manager(self):
+
+        with statsd.statsd_timethis('cm'):
+            time.sleep(0.1)
+
+        self.check_received_ms('test.cm:100.????|ms')
+        self.check_received('test.cm:1|c')
+
+
+if __name__ == '__main__':
+    from unittest import main
+    main()
--- a/flake8-ok-files.txt	Tue Apr 24 17:16:44 2018 +0200
+++ b/flake8-ok-files.txt	Tue Apr 24 15:21:18 2018 +0200
@@ -106,6 +106,7 @@
 cubicweb/test/unittest_rset.py
 cubicweb/test/unittest_rtags.py
 cubicweb/test/unittest_schema.py
+cubicweb/test/unittest_statsd.py
 cubicweb/test/unittest_toolsutils.py
 cubicweb/test/unittest_wfutils.py
 cubicweb/web/application.py