# Path: autopush/metrics.py
# class IMetrics(object):
#     """Metrics interface
# 
#     Each method except :meth:`__init__` and :meth:`start` must be implemented.
# 
#     Additional ``kwargs`` may be recorded as additional metric tags for metric
#     systems that support it, otherwise they should be ignored.
# 
#     """
#     def __init__(self, *args, **kwargs):
#         """Setup the metrics"""
# 
#     def start(self):
#         """Start any connection needed for metric transmission"""
# 
#     def increment(self, name, count=1, **kwargs):
#         """Increment a counter for a metric name"""
#         raise NotImplementedError("No increment implemented")
# 
#     def gauge(self, name, count, **kwargs):
#         """Record a gauge for a metric name"""
#         raise NotImplementedError("No gauge implemented")
# 
#     def timing(self, name, duration, **kwargs):
#         """Record a timing in ms for a metric name"""
#         raise NotImplementedError("No timing implemented")
# 
# class DatadogMetrics(object):
#     """DataDog Metric backend"""
#     def __init__(self, api_key, app_key, hostname, flush_interval=10,
#                  namespace="autopush"):
# 
#         datadog.initialize(api_key=api_key, app_key=app_key,
#                            host_name=hostname)
#         self._client = ThreadStats()
#         self._flush_interval = flush_interval
#         self._host = hostname
#         self._namespace = namespace
# 
#     def _prefix_name(self, name):
#         return "%s.%s" % (self._namespace, name)
# 
#     def start(self):
#         self._client.start(flush_interval=self._flush_interval,
#                            roll_up_interval=self._flush_interval)
# 
#     def increment(self, name, count=1, **kwargs):
#         self._client.increment(self._prefix_name(name), count, host=self._host,
#                                **kwargs)
# 
#     def gauge(self, name, count, **kwargs):
#         self._client.gauge(self._prefix_name(name), count, host=self._host,
#                            **kwargs)
# 
#     def timing(self, name, duration, **kwargs):
#         self._client.timing(self._prefix_name(name), value=duration,
#                             host=self._host, **kwargs)
# 
# class TwistedMetrics(object):
#     """Twisted implementation of statsd output"""
#     def __init__(self, statsd_host="localhost", statsd_port=8125):
#         self.client = TwistedStatsDClient.create(statsd_host, statsd_port)
#         self._metric = Metrics(connection=self.client, namespace="autopush")
# 
#     def start(self):
#         protocol = StatsDClientProtocol(self.client)
#         reactor.listenUDP(0, protocol)
# 
#     def increment(self, name, count=1, **kwargs):
#         self._metric.increment(name, count, **kwargs)
# 
#     def gauge(self, name, count, **kwargs):
#         self._metric.gauge(name, count, **kwargs)
# 
#     def timing(self, name, duration, **kwargs):
#         self._metric.timing(name, duration, **kwargs)
# 
# class SinkMetrics(IMetrics):
#     """Exists to ignore metrics when metrics are not active"""
#     def increment(self, name, count=1, **kwargs):
#         pass
# 
#     def gauge(self, name, count, **kwargs):
#         pass
# 
#     def timing(self, name, duration, **kwargs):
#         pass
# 
# def periodic_reporter(metrics, prefix=''):
#     # type: (IMetrics, Optional[str]) -> None
#     """Emit metrics on twisted's thread pool.
# 
#     Only meant to be called via a LoopingCall (TimerService).
# 
#     """
#     # unfortunately stats only available via the private '_team'
#     stats = reactor.getThreadPool()._team.statistics()
#     for attr in ('idleWorkerCount', 'busyWorkerCount', 'backloggedWorkCount'):
#         name = '{}{}twisted.threadpool.{}'.format(
#             prefix,
#             '.' if prefix else '',
#             attr
#         )
#         metrics.gauge(name, getattr(stats, attr))


import unittest
import twisted.internet.base
import pytest
from mock import Mock, patch, call
from autopush.metrics import (
    IMetrics,
    DatadogMetrics,
    TwistedMetrics,
    SinkMetrics,
    periodic_reporter,
)


class DatadogMetricsTestCase(unittest.TestCase):
    @patch("autopush.metrics.datadog")
    def test_basic(self, mock_dog):
        hostname = "localhost"

        m = DatadogMetrics("someapikey", "someappkey", namespace="testpush",
                           hostname="localhost")
        assert len(mock_dog.mock_calls) > 0
        m._client = Mock()
        m.start()
        m._client.start.assert_called_with(flush_interval=10,
                                           roll_up_interval=10)
        m.increment("test", 5)
        m._client.increment.assert_called_with("testpush.test", 5,
                                               host=hostname)
        m.gauge("connection_count", 200)
        m._client.gauge.assert_called_with("testpush.connection_count", 200,
                                           host=hostname)
        m.timing("lifespan", 113)
        m._client.timing.assert_called_with("testpush.lifespan", value=113,
                                            host=hostname)


class PeriodicReporterTestCase(unittest.TestCase):

    def test_periodic_reporter(self):
        metrics = Mock(spec=SinkMetrics)
        periodic_reporter(metrics)

# Path: autopush/metrics.py
# class IMetrics(object):
#     """Metrics interface
# 
#     Each method except :meth:`__init__` and :meth:`start` must be implemented.
# 
#     Additional ``kwargs`` may be recorded as additional metric tags for metric
#     systems that support it, otherwise they should be ignored.
# 
#     """
#     def __init__(self, *args, **kwargs):
#         """Setup the metrics"""
# 
#     def start(self):
#         """Start any connection needed for metric transmission"""
# 
#     def increment(self, name, count=1, **kwargs):
#         """Increment a counter for a metric name"""
#         raise NotImplementedError("No increment implemented")
# 
#     def gauge(self, name, count, **kwargs):
#         """Record a gauge for a metric name"""
#         raise NotImplementedError("No gauge implemented")
# 
#     def timing(self, name, duration, **kwargs):
#         """Record a timing in ms for a metric name"""
#         raise NotImplementedError("No timing implemented")
# 
# class DatadogMetrics(object):
#     """DataDog Metric backend"""
#     def __init__(self, api_key, app_key, hostname, flush_interval=10,
#                  namespace="autopush"):
# 
#         datadog.initialize(api_key=api_key, app_key=app_key,
#                            host_name=hostname)
#         self._client = ThreadStats()
#         self._flush_interval = flush_interval
#         self._host = hostname
#         self._namespace = namespace
# 
#     def _prefix_name(self, name):
#         return "%s.%s" % (self._namespace, name)
# 
#     def start(self):
#         self._client.start(flush_interval=self._flush_interval,
#                            roll_up_interval=self._flush_interval)
# 
#     def increment(self, name, count=1, **kwargs):
#         self._client.increment(self._prefix_name(name), count, host=self._host,
#                                **kwargs)
# 
#     def gauge(self, name, count, **kwargs):
#         self._client.gauge(self._prefix_name(name), count, host=self._host,
#                            **kwargs)
# 
#     def timing(self, name, duration, **kwargs):
#         self._client.timing(self._prefix_name(name), value=duration,
#                             host=self._host, **kwargs)
# 
# class TwistedMetrics(object):
#     """Twisted implementation of statsd output"""
#     def __init__(self, statsd_host="localhost", statsd_port=8125):
#         self.client = TwistedStatsDClient.create(statsd_host, statsd_port)
#         self._metric = Metrics(connection=self.client, namespace="autopush")
# 
#     def start(self):
#         protocol = StatsDClientProtocol(self.client)
#         reactor.listenUDP(0, protocol)
# 
#     def increment(self, name, count=1, **kwargs):
#         self._metric.increment(name, count, **kwargs)
# 
#     def gauge(self, name, count, **kwargs):
#         self._metric.gauge(name, count, **kwargs)
# 
#     def timing(self, name, duration, **kwargs):
#         self._metric.timing(name, duration, **kwargs)
# 
# class SinkMetrics(IMetrics):
#     """Exists to ignore metrics when metrics are not active"""
#     def increment(self, name, count=1, **kwargs):
#         pass
# 
#     def gauge(self, name, count, **kwargs):
#         pass
# 
#     def timing(self, name, duration, **kwargs):
#         pass
# 
# def periodic_reporter(metrics, prefix=''):
#     # type: (IMetrics, Optional[str]) -> None
#     """Emit metrics on twisted's thread pool.
# 
#     Only meant to be called via a LoopingCall (TimerService).
# 
#     """
#     # unfortunately stats only available via the private '_team'
#     stats = reactor.getThreadPool()._team.statistics()
#     for attr in ('idleWorkerCount', 'busyWorkerCount', 'backloggedWorkCount'):
#         name = '{}{}twisted.threadpool.{}'.format(
#             prefix,
#             '.' if prefix else '',
#             attr
#         )
#         metrics.gauge(name, getattr(stats, attr))


# Path: autopush/tests/test_metrics.py
import unittest
import twisted.internet.base
import pytest
from mock import Mock, patch, call
from autopush.metrics import (
    IMetrics,
    DatadogMetrics,
    TwistedMetrics,
    SinkMetrics,
    periodic_reporter,
)



class DatadogMetricsTestCase(unittest.TestCase):
    @patch("autopush.metrics.datadog")
    def test_basic(self, mock_dog):
        hostname = "localhost"

        m = DatadogMetrics("someapikey", "someappkey", namespace="testpush",
                           hostname="localhost")
        assert len(mock_dog.mock_calls) > 0
        m._client = Mock()
        m.start()
        m._client.start.assert_called_with(flush_interval=10,
                                           roll_up_interval=10)
        m.increment("test", 5)
        m._client.increment.assert_called_with("testpush.test", 5,
                                               host=hostname)
        m.gauge("connection_count", 200)
        m._client.gauge.assert_called_with("testpush.connection_count", 200,
                                           host=hostname)
        m.timing("lifespan", 113)
        m._client.timing.assert_called_with("testpush.lifespan", value=113,
                                            host=hostname)


class PeriodicReporterTestCase(unittest.TestCase):

    def test_periodic_reporter(self):
        metrics = Mock(spec=SinkMetrics)
        periodic_reporter(metrics)

        periodic_reporter(metrics, prefix='foo')