text: # (C) Datadog, Inc. 2016
  # All rights reserved
  # Licensed under Simplified BSD License (see LICENSE)
  import logging
  import os
  
  import pytest
  import mock
  import requests
  
  from datadog_checks.checks.prometheus import PrometheusCheck, UnknownFormatError
  from datadog_checks.utils.prometheus import parse_metric_family, metrics_pb2
  
  
  protobuf_content_type = 'application/vnd.google.protobuf; proto=io.prometheus.client.MetricFamily; encoding=delimited'
  
  
  class MockResponse:
      """
      MockResponse is used to simulate the object requests.Response commonly returned by requests.get
      """
      def __init__(self, content, content_type):
          self.content = content
          self.headers = {'Content-Type': content_type}
  
      def iter_lines(self, **_):
          for elt in self.content.split("\n"):
              yield elt
  
      def close(self):
          pass
  
  class SortedTagsPrometheusCheck(PrometheusCheck):
      """
      Tags are not sorted in a deterministic manner. There is no need to sort them normally.
      However, in order to ensure that the correct tags are submitted, the tags will need to be sorted
      This is a skeleton class to do that
      """
      def _finalize_tags_to_submit(self, _tags, metric_name, val, metric, custom_tags=None, hostname=None):
          """
          Format the finalized tags
          This is generally a noop, but it can be used to hook into _submit_gauge and change the tags before sending
          """
          return sorted(_tags)
  
  
  @pytest.fixture
  def mocked_prometheus_check():
      check = PrometheusCheck('prometheus_check', {}, {}, {})
      check.gauge = mock.MagicMock()
      check.rate = mock.MagicMock()
      check.log = logging.getLogger('datadog-prometheus.test')
      check.log.debug = mock.MagicMock()
      check.metrics_mapper = {'process_virtual_memory_bytes': 'process.vm.bytes'}
      check.NAMESPACE = 'prometheus'
  
      return check
  
  
  @pytest.fixture
  def p_check():
      return PrometheusCheck('prometheus_check', {}, {}, {})
  
  
  @pytest.fixture
  def sorted_tags_check():
      return SortedTagsPrometheusCheck('prometheus_check', {}, {}, {})
  
  
  @pytest.fixture
  def ref_gauge():
      ref_gauge = metrics_pb2.MetricFamily()
      ref_gauge.name = 'process_virtual_memory_bytes'
      ref_gauge.help = 'Virtual memory size in bytes.'
      ref_gauge.type = 1  # GAUGE
      _m = ref_gauge.metric.add()
      _m.gauge.value = 39211008.0
  
      return ref_gauge
  
  
  @pytest.fixture
  def bin_data():
      f_name = os.path.join(os.path.dirname(__file__), 'fixtures', 'prometheus', 'protobuf.bin')
      with open(f_name, 'rb') as f:
          bin_data = f.read()
          assert len(bin_data) == 51855
  
      return bin_data
  
  
  @pytest.fixture
  def text_data():
      # Loading test text data
      f_name = os.path.join(os.path.dirname(__file__), 'fixtures', 'prometheus', 'metrics.txt')
      with open(f_name, 'r') as f:
          text_data = f.read()
          assert len(text_data) == 14494
  
      return text_data
  
  
  def test_parse_metric_family():
      f_name = os.path.join(os.path.dirname(__file__), 'fixtures', 'prometheus', 'protobuf.bin')
      with open(f_name, 'rb') as f:
          data = f.read()
          assert len(data) == 51855
          messages = list(parse_metric_family(data))
          assert len(messages) == 61
          assert messages[-1].name == 'process_virtual_memory_bytes'
  
  
  def test_check(mocked_prometheus_check):
      """ Should not be implemented as it is the mother class """
      with pytest.raises(NotImplementedError):
          mocked_prometheus_check.check(None)
  
  
  def test_parse_metric_family_protobuf(bin_data, mocked_prometheus_check):
      response = MockResponse(bin_data, protobuf_content_type)
      check = mocked_prometheus_check
  
      messages = list(check.parse_metric_family(response))
  
      assert len(messages) == 61
      assert messages[-1].name == 'process_virtual_memory_bytes'
  
      # check type overriding is working
      # original type:
      assert messages[1].name == 'go_goroutines'
      assert messages[1].type == 1  # gauge
  
      # override the type:
      check.type_overrides = {"go_goroutines": "summary"}
  
      response = MockResponse(bin_data, protobuf_content_type)
  
      messages = list(check.parse_metric_family(response))
  
      assert len(messages) == 61
      assert messages[1].name == 'go_goroutines'
      assert messages[1].type == 2  # summary
  
  
  def test_parse_metric_family_text(text_data, mocked_prometheus_check):
      """ Test the high level method for loading metrics from text format """
      check = mocked_prometheus_check
  
      response = MockResponse(text_data, 'text/plain; version=0.0.4')
  
      messages = list(check.parse_metric_family(response))
      # total metrics are 41 but one is typeless and we expect it not to be
      # parsed...
      assert len(messages) == 40
      # ...unless the check ovverrides the type manually
      check.type_overrides = {"go_goroutines": "gauge"}
      response = MockResponse(text_data, 'text/plain; version=0.0.4')
      messages = list(check.parse_metric_family(response))
      assert len(messages) == 41
      # Tests correct parsing of counters
      _counter = metrics_pb2.MetricFamily()
      _counter.name = 'skydns_skydns_dns_cachemiss_count_total'
      _counter.help = 'Counter of DNS requests that result in a cache miss.'
      _counter.type = 0  # COUNTER
      _c = _counter.metric.add()
      _c.counter.value = 1359194.0
      _lc = _c.label.add()
      _lc.name = 'cache'
      _lc.value = 'response'
      assert _counter in messages
      # Tests correct parsing of gauges
      _gauge = metrics_pb2.MetricFamily()
      _gauge.name = 'go_memstats_heap_alloc_bytes'
      _gauge.help = 'Number of heap bytes allocated and still in use.'
      _gauge.type = 1  # GAUGE
      _gauge.metric.add().gauge.value = 6396288.0
      assert _gauge in messages
      # Tests correct parsing of summaries
      _summary = metrics_pb2.MetricFamily()
      _summary.name = 'http_response_size_bytes'
      _summary.help = 'The HTTP response sizes in bytes.'
      _summary.type = 2  # SUMMARY
      _sm = _summary.metric.add()
      _lsm = _sm.label.add()
      _lsm.name = 'handler'
      _lsm.value = 'prometheus'
      _sm.summary.sample_count = 25
      _sm.summary.sample_sum = 147728.0
      _sq1 = _sm.summary.quantile.add()
      _sq1.quantile = 0.5
      _sq1.value = 21470.0
      _sq2 = _sm.summary.quantile.add()
      _sq2.quantile = 0.9
      _sq2.value = 21470.0
      _sq3 = _sm.summary.quantile.add()
      _sq3.quantile = 0.99
      _sq3.value = 21470.0
      assert _summary in messages
      # Tests correct parsing of histograms
      _histo = metrics_pb2.MetricFamily()
      _histo.name = 'skydns_skydns_dns_response_size_bytes'
      _histo.help = 'Size of the returns response in bytes.'
      _histo.type = 4  # HISTOGRAM
      _sample_data = [
          {'ct': 1359194, 'sum': 199427281.0, 'lbl': {'system': 'auth'},
           'buckets': {0.0: 0, 512.0: 1359194, 1024.0: 1359194,
                       1500.0: 1359194, 2048.0: 1359194, float('+Inf'): 1359194}},
          {'ct': 520924, 'sum': 41527128.0, 'lbl': {'system': 'recursive'},
           'buckets': {0.0: 0, 512.0: 520924, 1024.0: 520924, 1500.0: 520924,
                       2048.0: 520924, float('+Inf'): 520924}},
          {'ct': 67648, 'sum': 6075182.0, 'lbl': {'system': 'reverse'},
           'buckets': {0.0: 0, 512.0: 67648, 1024.0: 67648, 1500.0: 67648,
                       2048.0: 67648, float('+Inf'): 67648}},
      ]
      for _data in _sample_data:
          _h = _histo.metric.add()
          _h.histogram.sample_count = _data['ct']
          _h.histogram.sample_sum = _data['sum']
          for k, v in _data['lbl'].items():
              _lh = _h.label.add()
              _lh.name = k
              _lh.value = v
          for _b in sorted(_data['buckets'].iterkeys()):
              _subh = _h.histogram.bucket.add()
              _subh.upper_bound = _b
              _subh.cumulative_count = _data['buckets'][_b]
      assert _histo in messages
  
  
  def test_parse_metric_family_unsupported(bin_data, mocked_prometheus_check):
      check = mocked_prometheus_check
      with pytest.raises(UnknownFormatError):
          response = MockResponse(bin_data, 'application/json')
          list(check.parse_metric_family(response))
  
  
  def test_process(bin_data, mocked_prometheus_check, ref_gauge):
      endpoint = "http://fake.endpoint:10055/metrics"
      check = mocked_prometheus_check
  
      check.poll = mock.MagicMock(return_value=MockResponse(bin_data, protobuf_content_type))
      check.process_metric = mock.MagicMock()
      check.process(endpoint, instance=None)
      check.poll.assert_called_with(endpoint)
      check.process_metric.assert_called_with(ref_gauge, instance=None)
  
  
  def test_process_send_histograms_buckets(bin_data, mocked_prometheus_check, ref_gauge):
      """ Checks that the send_histograms_buckets parameter is passed along """
      endpoint = "http://fake.endpoint:10055/metrics"
      check = mocked_prometheus_check
      check.poll = mock.MagicMock(
          return_value=MockResponse(bin_data, protobuf_content_type))
      check.process_metric = mock.MagicMock()
      check.process(endpoint, send_histograms_buckets=False, instance=None)
      check.poll.assert_called_with(endpoint)
      check.process_metric.assert_called_with(ref_gauge, instance=None, send_histograms_buckets=False)
  
  
  def test_process_send_monotonic_counter(bin_data, mocked_prometheus_check, ref_gauge):
      """ Checks that the send_monotonic_counter parameter is passed along """
      endpoint = "http://fake.endpoint:10055/metrics"
      check = mocked_prometheus_check
      check.poll = mock.MagicMock(
          return_value=MockResponse(bin_data, protobuf_content_type))
      check.process_metric = mock.MagicMock()
      check.process(endpoint, send_monotonic_counter=False, instance=None)
      check.poll.assert_called_with(endpoint)
      check.process_metric.assert_called_with(ref_gauge, instance=None, send_monotonic_counter=False)
  
  def test_process_instance_with_tags(bin_data, mocked_prometheus_check, ref_gauge):
      """ Checks that an instances with tags passes them as custom tag """
      endpoint = "http://fake.endpoint:10055/metrics"
      check = mocked_prometheus_check
      check.poll = mock.MagicMock(
          return_value=MockResponse(bin_data, protobuf_content_type))
      check.process_metric = mock.MagicMock()
      instance = {'endpoint': 'IgnoreMe', 'tags': ['tag1:tagValue1', 'tag2:tagValue2']}
      check.process(endpoint, instance=instance)
      check.poll.assert_called_with(endpoint)
      check.process_metric.assert_called_with(ref_gauge, custom_tags=['tag1:tagValue1', 'tag2:tagValue2'],
                                              instance=instance)
  
  
  def test_process_metric_gauge(mocked_prometheus_check, ref_gauge):
      """ Gauge ref submission """
      check = mocked_prometheus_check
      check._dry_run = False
      check.process_metric(ref_gauge)
      check.gauge.assert_called_with('prometheus.process.vm.bytes', 39211008.0, [], hostname=None)
  
  
  def test_process_metric_filtered(mocked_prometheus_check):
      """ Metric absent from the metrics_mapper """
      filtered_gauge = metrics_pb2.MetricFamily()
      filtered_gauge.name = "process_start_time_seconds"
      filtered_gauge.help = "Start time of the process since unix epoch in seconds."
      filtered_gauge.type = 1  # GAUGE
      _m = filtered_gauge.metric.add()
      _m.gauge.value = 39211008.0
      check = mocked_prometheus_check
      check._dry_run = False
      check.process_metric(filtered_gauge)
      check.log.debug.assert_called_with(
          "Unable to handle metric: process_start_time_seconds - error: 'PrometheusCheck' object has no attribute 'process_start_time_seconds'")
      check.gauge.assert_not_called()
  
  
  def test_poll_protobuf(mocked_prometheus_check, bin_data):
      """ Tests poll using the protobuf format """
      check = mocked_prometheus_check
      mock_response = mock.MagicMock(
          status_code=200,
          content=bin_data,
          headers={'Content-Type': protobuf_content_type})
      p = mock.patch('requests.get', return_value=mock_response, __name__="get")
      p.start()
      response = check.poll("http://fake.endpoint:10055/metrics")
      messages = list(check.parse_metric_family(response))
      assert len(messages) == 61
      assert messages[-1].name == 'process_virtual_memory_bytes'
      p.stop()
  
  
  def test_poll_text_plain(mocked_prometheus_check, text_data):
      """Tests poll using the text format"""
      check = mocked_prometheus_check
      mock_response = mock.MagicMock(
          status_code=200,
          iter_lines=lambda **kwargs: text_data.split("\n"),
          headers={'Content-Type': "text/plain"})
      p = mock.patch('requests.get', return_value=mock_response, __name__="get")
      p.start()
      response = check.poll("http://fake.endpoint:10055/metrics")
      messages = list(check.parse_metric_family(response))
      messages.sort(key=lambda x: x.name)
      assert len(messages) == 40
      assert messages[-1].name == 'skydns_skydns_dns_response_size_bytes'
      p.stop()
  
  
  def test_submit_gauge_with_labels(mocked_prometheus_check, ref_gauge):
      """ submitting metrics that contain labels should result in tags on the gauge call """
      _l1 = ref_gauge.metric[0].label.add()
      _l1.name = 'my_1st_label'
      _l1.value = 'my_1st_label_value'
      _l2 = ref_gauge.metric[0].label.add()
      _l2.name = 'my_2nd_label'
      _l2.value = 'my_2nd_label_value'
      check = mocked_prometheus_check
      check._submit(check.metrics_mapper[ref_gauge.name], ref_gauge)
      check.gauge.assert_called_with('prometheus.process.vm.bytes', 39211008.0,
                                     ['my_1st_label:my_1st_label_value', 'my_2nd_label:my_2nd_label_value'],
                                     hostname=None)
  
  
  def test_submit_gauge_with_labels_and_hostname_override(mocked_prometheus_check, ref_gauge):
      """ submitting metrics that contain labels should result in tags on the gauge call """
      _l1 = ref_gauge.metric[0].label.add()
      _l1.name = 'my_1st_label'
      _l1.value = 'my_1st_label_value'
      _l2 = ref_gauge.metric[0].label.add()
      _l2.name = 'node'
      _l2.value = 'foo'
      check = mocked_prometheus_check
      check.label_to_hostname = 'node'
      check._submit(check.metrics_mapper[ref_gauge.name], ref_gauge)
      check.gauge.assert_called_with('prometheus.process.vm.bytes', 39211008.0,
                                     ['my_1st_label:my_1st_label_value', 'node:foo'],
                                     hostname="foo")
  
  
  def test_submit_gauge_with_labels_and_hostname_already_overridden(mocked_prometheus_check, ref_gauge):
      """ submitting metrics that contain labels should result in tags on the gauge call """
      _l1 = ref_gauge.metric[0].label.add()
      _l1.name = 'my_1st_label'
      _l1.value = 'my_1st_label_value'
      _l2 = ref_gauge.metric[0].label.add()
      _l2.name = 'node'
      _l2.value = 'foo'
      check = mocked_prometheus_check
      check.label_to_hostname = 'node'
      check._submit(check.metrics_mapper[ref_gauge.name], ref_gauge, hostname="bar")
      check.gauge.assert_called_with('prometheus.process.vm.bytes', 39211008.0,
                                     ['my_1st_label:my_1st_label_value', 'node:foo'],
                                     hostname="bar")
  
  
  def test_labels_not_added_as_tag_once_for_each_metric(mocked_prometheus_check, ref_gauge):
      _l1 = ref_gauge.metric[0].label.add()
      _l1.name = 'my_1st_label'
      _l1.value = 'my_1st_label_value'
      _l2 = ref_gauge.metric[0].label.add()
      _l2.name = 'my_2nd_label'
      _l2.value = 'my_2nd_label_value'
      tags = ['test']
      check = mocked_prometheus_check
      check._submit(check.metrics_mapper[ref_gauge.name], ref_gauge, custom_tags=tags)
      # Call a second time to check that the labels were not added once more to the tags list and
      # avoid regression on https://github.com/DataDog/dd-agent/pull/3359
      check._submit(check.metrics_mapper[ref_gauge.name], ref_gauge, custom_tags=tags)
      check.gauge.assert_called_with('prometheus.process.vm.bytes', 39211008.0,
                                     ['test', 'my_1st_label:my_1st_label_value',
                                      'my_2nd_label:my_2nd_label_value'], hostname=None)
  
  
  def test_submit_gauge_with_custom_tags(mocked_prometheus_check, ref_gauge):
      """ Providing custom tags should add them as is on the gauge call """
      tags = ['env:dev', 'app:my_pretty_app']
      check = mocked_prometheus_check
      check._submit(check.metrics_mapper[ref_gauge.name], ref_gauge, custom_tags=tags)
      check.gauge.assert_called_with('prometheus.process.vm.bytes', 39211008.0,
                                     ['env:dev', 'app:my_pretty_app'], hostname=None)
  
  
  def test_submit_gauge_with_labels_mapper(mocked_prometheus_check, ref_gauge):
      """
      Submitting metrics that contain labels mappers should result in tags
      on the gauge call with transformed tag names
      """
      _l1 = ref_gauge.metric[0].label.add()
      _l1.name = 'my_1st_label'
      _l1.value = 'my_1st_label_value'
      _l2 = ref_gauge.metric[0].label.add()
      _l2.name = 'my_2nd_label'
      _l2.value = 'my_2nd_label_value'
      check = mocked_prometheus_check
      check.labels_mapper = {'my_1st_label': 'transformed_1st', 'non_existent': 'should_not_matter',
                             'env': 'dont_touch_custom_tags'}
  
      tags = ['env:dev', 'app:my_pretty_app']
      check._submit(check.metrics_mapper[ref_gauge.name], ref_gauge, custom_tags=tags)
      check.gauge.assert_called_with('prometheus.process.vm.bytes', 39211008.0,
                                     ['env:dev', 'app:my_pretty_app', 'transformed_1st:my_1st_label_value',
                                      'my_2nd_label:my_2nd_label_value'], hostname=None)
  
  
  def test_submit_gauge_with_exclude_labels(mocked_prometheus_check, ref_gauge):
      """
      Submitting metrics when filtering with exclude_labels should end up with
      a filtered tags list
      """
      _l1 = ref_gauge.metric[0].label.add()
      _l1.name = 'my_1st_label'
      _l1.value = 'my_1st_label_value'
      _l2 = ref_gauge.metric[0].label.add()
      _l2.name = 'my_2nd_label'
      _l2.value = 'my_2nd_label_value'
      check = mocked_prometheus_check
      check.labels_mapper = {'my_1st_label': 'transformed_1st', 'non_existent': 'should_not_matter',
                             'env': 'dont_touch_custom_tags'}
      tags = ['env:dev', 'app:my_pretty_app']
      check.exclude_labels = ['my_2nd_label', 'whatever_else', 'env']  # custom tags are not filtered out
      check._submit(check.metrics_mapper[ref_gauge.name], ref_gauge, custom_tags=tags)
      check.gauge.assert_called_with('prometheus.process.vm.bytes', 39211008.0,
                                     ['env:dev', 'app:my_pretty_app', 'transformed_1st:my_1st_label_value'],
                                     hostname=None)
  
  
  def test_submit_counter(mocked_prometheus_check):
      _counter = metrics_pb2.MetricFamily()
      _counter.name = 'my_counter'
      _counter.help = 'Random counter'
      _counter.type = 0  # COUNTER
      _met = _counter.metric.add()
      _met.counter.value = 42
      check = mocked_prometheus_check
      check._submit('custom.counter', _counter)
      check.gauge.assert_called_with('prometheus.custom.counter', 42, [], hostname=None)
  
  
  def test_submits_summary(mocked_prometheus_check):
      _sum = metrics_pb2.MetricFamily()
      _sum.name = 'my_summary'
      _sum.help = 'Random summary'
      _sum.type = 2  # SUMMARY
      _met = _sum.metric.add()
      _met.summary.sample_count = 42
      _met.summary.sample_sum = 3.14
      _q1 = _met.summary.quantile.add()
      _q1.quantile = 10.0
      _q1.value = 3
      _q2 = _met.summary.quantile.add()
      _q2.quantile = 4.0
      _q2.value = 5
      check = mocked_prometheus_check
      check._submit('custom.summary', _sum)
      check.gauge.assert_has_calls([
          mock.call('prometheus.custom.summary.count', 42, [], hostname=None),
          mock.call('prometheus.custom.summary.sum', 3.14, [], hostname=None),
          mock.call('prometheus.custom.summary.quantile', 3, ['quantile:10.0'], hostname=None),
          mock.call('prometheus.custom.summary.quantile', 5, ['quantile:4.0'], hostname=None)
      ])
  
  
  def test_submit_histogram(mocked_prometheus_check):
      _histo = metrics_pb2.MetricFamily()
      _histo.name = 'my_histogram'
      _histo.help = 'Random histogram'
      _histo.type = 4  # HISTOGRAM
      _met = _histo.metric.add()
      _met.histogram.sample_count = 42
      _met.histogram.sample_sum = 3.14
      _b1 = _met.histogram.bucket.add()
      _b1.upper_bound = 12.7
      _b1.cumulative_count = 33
      _b2 = _met.histogram.bucket.add()
      _b2.upper_bound = 18.2
      _b2.cumulative_count = 666
      check = mocked_prometheus_check
      check._submit('custom.histogram', _histo)
      check.gauge.assert_has_calls([
          mock.call('prometheus.custom.histogram.count', 42, [], hostname=None),
          mock.call('prometheus.custom.histogram.sum', 3.14, [], hostname=None),
          mock.call('prometheus.custom.histogram.count', 33, ['upper_bound:12.7'], hostname=None),
          mock.call('prometheus.custom.histogram.count', 666, ['upper_bound:18.2'], hostname=None)
      ])
  
  def test_submit_rate(mocked_prometheus_check):
      _rate = metrics_pb2.MetricFamily()
      _rate.name = 'my_rate'
      _rate.help = 'Random rate'
      _rate.type = 1  # GAUGE
      _met = _rate.metric.add()
      _met.gauge.value = 42
      check = mocked_prometheus_check
      check.rate_metrics = ["my_rate"]
      check._submit('custom.rate', _rate)
      check.rate.assert_called_with('prometheus.custom.rate', 42, [], hostname=None)
  
  def test_parse_one_gauge(p_check):
      """
      name: "etcd_server_has_leader"
      help: "Whether or not a leader exists. 1 is existence, 0 is not."
      type: GAUGE
      metric {
        gauge {
          value: 1.0
        }
      }
      """
      text_data = (
          "# HELP etcd_server_has_leader Whether or not a leader exists. 1 is existence, 0 is not.\n"
          "# TYPE etcd_server_has_leader gauge\n"
          "etcd_server_has_leader 1\n")
  
      expected_etcd_metric = metrics_pb2.MetricFamily()
      expected_etcd_metric.help = "Whether or not a leader exists. 1 is existence, 0 is not."
      expected_etcd_metric.name = "etcd_server_has_leader"
      expected_etcd_metric.type = 1
      expected_etcd_metric.metric.add().gauge.value = 1
  
      # Iter on the generator to get all metrics
      response = MockResponse(text_data, 'text/plain; version=0.0.4')
      check = p_check
      metrics = [k for k in check.parse_metric_family(response)]
  
      assert 1 == len(metrics)
      current_metric = metrics[0]
      assert expected_etcd_metric == current_metric
  
      # Remove the old metric and add a new one with a different value
      expected_etcd_metric.metric.pop()
      expected_etcd_metric.metric.add().gauge.value = 0
      assert expected_etcd_metric != current_metric
  
      # Re-add the expected value but as different type: it should works
      expected_etcd_metric.metric.pop()
      expected_etcd_metric.metric.add().gauge.value = 1.0
      assert expected_etcd_metric == current_metric
  
  
  def test_parse_one_counter(p_check):
      """
      name: "go_memstats_mallocs_total"
      help: "Total number of mallocs."
      type: COUNTER
      metric {
        counter {
          value: 18713.0
        }
      }
      """
      text_data = (
          "# HELP go_memstats_mallocs_total Total number of mallocs.\n"
          "# TYPE go_memstats_mallocs_total counter\n"
          "go_memstats_mallocs_total 18713\n")
  
      expected_etcd_metric = metrics_pb2.MetricFamily()
      expected_etcd_metric.help = "Total number of mallocs."
      expected_etcd_metric.name = "go_memstats_mallocs_total"
      expected_etcd_metric.type = 0
      expected_etcd_metric.metric.add().counter.value = 18713
  
      # Iter on the generator to get all metrics
      response = MockResponse(text_data, 'text/plain; version=0.0.4')
      check = p_check
      metrics = [k for k in check.parse_metric_family(response)]
  
      assert 1 == len(metrics)
      current_metric = metrics[0]
      assert expected_etcd_metric == current_metric
  
      # Remove the old metric and add a new one with a different value
      expected_etcd_metric.metric.pop()
      expected_etcd_metric.metric.add().counter.value = 18714
      assert expected_etcd_metric != current_metric
  
  
  def test_parse_one_histograms_with_label(p_check):
      text_data = (
          '# HELP etcd_disk_wal_fsync_duration_seconds The latency distributions of fsync called by wal.\n'
          '# TYPE etcd_disk_wal_fsync_duration_seconds histogram\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="0.001"} 2\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="0.002"} 2\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="0.004"} 2\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="0.008"} 2\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="0.016"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="0.032"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="0.064"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="0.128"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="0.256"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="0.512"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="1.024"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="2.048"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="4.096"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="8.192"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{app="vault",le="+Inf"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_sum{app="vault"} 0.026131671\n'
          'etcd_disk_wal_fsync_duration_seconds_count{app="vault"} 4\n')
  
      expected_etcd_vault_metric = metrics_pb2.MetricFamily()
      expected_etcd_vault_metric.help = "The latency distributions of fsync called by wal."
      expected_etcd_vault_metric.name = "etcd_disk_wal_fsync_duration_seconds"
      expected_etcd_vault_metric.type = 4
  
      histogram_metric = expected_etcd_vault_metric.metric.add()
  
      # Label for app vault
      summary_label = histogram_metric.label.add()
      summary_label.name, summary_label.value = "app", "vault"
  
      for upper_bound, cumulative_count in [
          (0.001, 2),
          (0.002, 2),
          (0.004, 2),
          (0.008, 2),
          (0.016, 4),
          (0.032, 4),
          (0.064, 4),
          (0.128, 4),
          (0.256, 4),
          (0.512, 4),
          (1.024, 4),
          (2.048, 4),
          (4.096, 4),
          (8.192, 4),
          (float('inf'), 4),
      ]:
          bucket = histogram_metric.histogram.bucket.add()
          bucket.upper_bound = upper_bound
          bucket.cumulative_count = cumulative_count
  
      # Root histogram sample
      histogram_metric.histogram.sample_count = 4
      histogram_metric.histogram.sample_sum = 0.026131671
  
      # Iter on the generator to get all metrics
      response = MockResponse(text_data, 'text/plain; version=0.0.4')
      check = p_check
      metrics = [k for k in check.parse_metric_family(response)]
  
      assert 1 == len(metrics)
      current_metric = metrics[0]
      assert expected_etcd_vault_metric == current_metric
  
  
  def test_parse_one_histogram(p_check):
      """
      name: "etcd_disk_wal_fsync_duration_seconds"
      help: "The latency distributions of fsync called by wal."
      type: HISTOGRAM
      metric {
        histogram {
          sample_count: 4
          sample_sum: 0.026131671
          bucket {
            cumulative_count: 2
            upper_bound: 0.001
          }
          bucket {
            cumulative_count: 2
            upper_bound: 0.002
          }
          bucket {
            cumulative_count: 2
            upper_bound: 0.004
          }
          bucket {
            cumulative_count: 2
            upper_bound: 0.008
          }
          bucket {
            cumulative_count: 4
            upper_bound: 0.016
          }
          bucket {
            cumulative_count: 4
            upper_bound: 0.032
          }
          bucket {
            cumulative_count: 4
            upper_bound: 0.064
          }
          bucket {
            cumulative_count: 4
            upper_bound: 0.128
          }
          bucket {
            cumulative_count: 4
            upper_bound: 0.256
          }
          bucket {
            cumulative_count: 4
            upper_bound: 0.512
          }
          bucket {
            cumulative_count: 4
            upper_bound: 1.024
          }
          bucket {
            cumulative_count: 4
            upper_bound: 2.048
          }
          bucket {
            cumulative_count: 4
            upper_bound: 4.096
          }
          bucket {
            cumulative_count: 4
            upper_bound: 8.192
          }
          bucket {
            cumulative_count: 4
            upper_bound: inf
          }
        }
      }
      """
      text_data = (
          '# HELP etcd_disk_wal_fsync_duration_seconds The latency distributions of fsync called by wal.\n'
          '# TYPE etcd_disk_wal_fsync_duration_seconds histogram\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="0.001"} 2\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="0.002"} 2\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="0.004"} 2\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="0.008"} 2\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="0.016"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="0.032"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="0.064"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="0.128"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="0.256"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="0.512"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="1.024"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="2.048"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="4.096"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="8.192"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{le="+Inf"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_sum 0.026131671\n'
          'etcd_disk_wal_fsync_duration_seconds_count 4\n')
  
      expected_etcd_metric = metrics_pb2.MetricFamily()
      expected_etcd_metric.help = "The latency distributions of fsync called by wal."
      expected_etcd_metric.name = "etcd_disk_wal_fsync_duration_seconds"
      expected_etcd_metric.type = 4
  
      histogram_metric = expected_etcd_metric.metric.add()
      for upper_bound, cumulative_count in [
          (0.001, 2),
          (0.002, 2),
          (0.004, 2),
          (0.008, 2),
          (0.016, 4),
          (0.032, 4),
          (0.064, 4),
          (0.128, 4),
          (0.256, 4),
          (0.512, 4),
          (1.024, 4),
          (2.048, 4),
          (4.096, 4),
          (8.192, 4),
          (float('inf'), 4),
      ]:
          bucket = histogram_metric.histogram.bucket.add()
          bucket.upper_bound = upper_bound
          bucket.cumulative_count = cumulative_count
  
      # Root histogram sample
      histogram_metric.histogram.sample_count = 4
      histogram_metric.histogram.sample_sum = 0.026131671
  
      # Iter on the generator to get all metrics
      response = MockResponse(text_data, 'text/plain; version=0.0.4')
      check = p_check
      metrics = [k for k in check.parse_metric_family(response)]
  
      assert 1 == len(metrics)
      current_metric = metrics[0]
      assert expected_etcd_metric == current_metric
  
  
  def test_parse_two_histograms_with_label(p_check):
      text_data = (
          '# HELP etcd_disk_wal_fsync_duration_seconds The latency distributions of fsync called by wal.\n'
          '# TYPE etcd_disk_wal_fsync_duration_seconds histogram\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="0.001"} 2\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="0.002"} 2\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="0.004"} 2\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="0.008"} 2\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="0.016"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="0.032"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="0.064"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="0.128"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="0.256"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="0.512"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="1.024"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="2.048"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="4.096"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="8.192"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="vault",le="+Inf"} 4\n'
          'etcd_disk_wal_fsync_duration_seconds_sum{kind="fs",app="vault"} 0.026131671\n'
          'etcd_disk_wal_fsync_duration_seconds_count{kind="fs",app="vault"} 4\n'
  
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="0.001"} 718\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="0.002"} 740\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="0.004"} 743\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="0.008"} 748\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="0.016"} 751\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="0.032"} 751\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="0.064"} 751\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="0.128"} 751\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="0.256"} 751\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="0.512"} 751\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="1.024"} 751\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="2.048"} 751\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="4.096"} 751\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="8.192"} 751\n'
          'etcd_disk_wal_fsync_duration_seconds_bucket{kind="fs",app="kubernetes",le="+Inf"} 751\n'
          'etcd_disk_wal_fsync_duration_seconds_sum{kind="fs",app="kubernetes"} 0.3097010759999998\n'
          'etcd_disk_wal_fsync_duration_seconds_count{kind="fs",app="kubernetes"} 751\n')
  
      expected_etcd_metric = metrics_pb2.MetricFamily()
      expected_etcd_metric.help = "The latency distributions of fsync called by wal."
      expected_etcd_metric.name = "etcd_disk_wal_fsync_duration_seconds"
      expected_etcd_metric.type = 4
  
      # Vault
      histogram_metric = expected_etcd_metric.metric.add()
  
      # Label for app vault
      summary_label = histogram_metric.label.add()
      summary_label.name, summary_label.value = "kind", "fs"
      summary_label = histogram_metric.label.add()
      summary_label.name, summary_label.value = "app", "vault"
  
      for upper_bound, cumulative_count in [
          (0.001, 2),
          (0.002, 2),
          (0.004, 2),
          (0.008, 2),
          (0.016, 4),
          (0.032, 4),
          (0.064, 4),
          (0.128, 4),
          (0.256, 4),
          (0.512, 4),
          (1.024, 4),
          (2.048, 4),
          (4.096, 4),
          (8.192, 4),
          (float('inf'), 4),
      ]:
          bucket = histogram_metric.histogram.bucket.add()
          bucket.upper_bound = upper_bound
          bucket.cumulative_count = cumulative_count
  
      # Root histogram sample
      histogram_metric.histogram.sample_count = 4
      histogram_metric.histogram.sample_sum = 0.026131671
  
      # Kubernetes
      histogram_metric = expected_etcd_metric.metric.add()
  
      # Label for app kubernetes
      summary_label = histogram_metric.label.add()
      summary_label.name, summary_label.value = "kind", "fs"
      summary_label = histogram_metric.label.add()
      summary_label.name, summary_label.value = "app", "kubernetes"
  
      for upper_bound, cumulative_count in [
          (0.001, 718),
          (0.002, 740),
          (0.004, 743),
          (0.008, 748),
          (0.016, 751),
          (0.032, 751),
          (0.064, 751),
          (0.128, 751),
          (0.256, 751),
          (0.512, 751),
          (1.024, 751),
          (2.048, 751),
          (4.096, 751),
          (8.192, 751),
          (float('inf'), 751),
      ]:
          bucket = histogram_metric.histogram.bucket.add()
          bucket.upper_bound = upper_bound
          bucket.cumulative_count = cumulative_count
  
      # Root histogram sample
      histogram_metric.histogram.sample_count = 751
      histogram_metric.histogram.sample_sum = 0.3097010759999998
  
      # Iter on the generator to get all metrics
      response = MockResponse(text_data, 'text/plain; version=0.0.4')
      check = p_check
      metrics = [k for k in check.parse_metric_family(response)]
  
      assert 1 == len(metrics)
  
      current_metric = metrics[0]
      # in metrics with more than one label
      # the labels don't always get parsed in a deterministic order
      # deconstruct the metric to ensure it's equal
      assert expected_etcd_metric.help == current_metric.help
      assert expected_etcd_metric.name == current_metric.name
      assert expected_etcd_metric.type == current_metric.type
      for idx in xrange(len(expected_etcd_metric.metric)):
          assert expected_etcd_metric.metric[idx].summary == current_metric.metric[idx].summary
          for label in expected_etcd_metric.metric[idx].label:
              assert label in current_metric.metric[idx].label
  
  
  def test_parse_one_summary(p_check):
      """
      name: "http_response_size_bytes"
      help: "The HTTP response sizes in bytes."
      type: SUMMARY
      metric {
        label {
          name: "handler"
          value: "prometheus"
        }
        summary {
          sample_count: 5
          sample_sum: 120512.0
          quantile {
            quantile: 0.5
            value: 24547.0
          }
          quantile {
            quantile: 0.9
            value: 25763.0
          }
          quantile {
            quantile: 0.99
            value: 25763.0
          }
        }
      }
      """
      text_data = (
          '# HELP http_response_size_bytes The HTTP response sizes in bytes.\n'
          '# TYPE http_response_size_bytes summary\n'
          'http_response_size_bytes{handler="prometheus",quantile="0.5"} 24547\n'
          'http_response_size_bytes{handler="prometheus",quantile="0.9"} 25763\n'
          'http_response_size_bytes{handler="prometheus",quantile="0.99"} 25763\n'
          'http_response_size_bytes_sum{handler="prometheus"} 120512\n'
          'http_response_size_bytes_count{handler="prometheus"} 5\n')
  
      expected_etcd_metric = metrics_pb2.MetricFamily()
      expected_etcd_metric.help = "The HTTP response sizes in bytes."
      expected_etcd_metric.name = "http_response_size_bytes"
      expected_etcd_metric.type = 2
  
      summary_metric = expected_etcd_metric.metric.add()
  
      # Label for prometheus handler
      summary_label = summary_metric.label.add()
      summary_label.name, summary_label.value = "handler", "prometheus"
  
      # Root summary sample
      summary_metric.summary.sample_count = 5
      summary_metric.summary.sample_sum = 120512
  
      # Create quantiles 0.5, 0.9, 0.99
      quantile_05 = summary_metric.summary.quantile.add()
      quantile_05.quantile = 0.5
      quantile_05.value = 24547
  
      quantile_09 = summary_metric.summary.quantile.add()
      quantile_09.quantile = 0.9
      quantile_09.value = 25763
  
      quantile_099 = summary_metric.summary.quantile.add()
      quantile_099.quantile = 0.99
      quantile_099.value = 25763
  
      # Iter on the generator to get all metrics
      response = MockResponse(text_data, 'text/plain; version=0.0.4')
      check = p_check
      metrics = [k for k in check.parse_metric_family(response)]
  
      assert 1 == len(metrics)
      current_metric = metrics[0]
      assert expected_etcd_metric == current_metric
  
  
  def test_parse_two_summaries_with_labels(p_check):
      text_data = (
          '# HELP http_response_size_bytes The HTTP response sizes in bytes.\n'
          '# TYPE http_response_size_bytes summary\n'
          'http_response_size_bytes{from="internet",handler="prometheus",quantile="0.5"} 24547\n'
          'http_response_size_bytes{from="internet",handler="prometheus",quantile="0.9"} 25763\n'
          'http_response_size_bytes{from="internet",handler="prometheus",quantile="0.99"} 25763\n'
          'http_response_size_bytes_sum{from="internet",handler="prometheus"} 120512\n'
          'http_response_size_bytes_count{from="internet",handler="prometheus"} 5\n'
  
          'http_response_size_bytes{from="cluster",handler="prometheus",quantile="0.5"} 24615\n'
          'http_response_size_bytes{from="cluster",handler="prometheus",quantile="0.9"} 24627\n'
          'http_response_size_bytes{from="cluster",handler="prometheus",quantile="0.99"} 24627\n'
          'http_response_size_bytes_sum{from="cluster",handler="prometheus"} 94913\n'
          'http_response_size_bytes_count{from="cluster",handler="prometheus"} 4\n')
  
      expected_etcd_metric = metrics_pb2.MetricFamily()
      expected_etcd_metric.help = "The HTTP response sizes in bytes."
      expected_etcd_metric.name = "http_response_size_bytes"
      expected_etcd_metric.type = 2
  
      # Metric from internet #
      summary_metric_from_internet = expected_etcd_metric.metric.add()
  
      # Label for prometheus handler
      summary_label = summary_metric_from_internet.label.add()
      summary_label.name, summary_label.value = "handler", "prometheus"
  
      summary_label = summary_metric_from_internet.label.add()
      summary_label.name, summary_label.value = "from", "internet"
  
      # Root summary sample
      summary_metric_from_internet.summary.sample_count = 5
      summary_metric_from_internet.summary.sample_sum = 120512
  
      # Create quantiles 0.5, 0.9, 0.99
      quantile_05 = summary_metric_from_internet.summary.quantile.add()
      quantile_05.quantile = 0.5
      quantile_05.value = 24547
  
      quantile_09 = summary_metric_from_internet.summary.quantile.add()
      quantile_09.quantile = 0.9
      quantile_09.value = 25763
  
      quantile_099 = summary_metric_from_internet.summary.quantile.add()
      quantile_099.quantile = 0.99
      quantile_099.value = 25763
  
      # Metric from cluster #
      summary_metric_from_cluster = expected_etcd_metric.metric.add()
  
      # Label for prometheus handler
      summary_label = summary_metric_from_cluster.label.add()
      summary_label.name, summary_label.value = "handler", "prometheus"
  
      summary_label = summary_metric_from_cluster.label.add()
      summary_label.name, summary_label.value = "from", "cluster"
  
      # Root summary sample
      summary_metric_from_cluster.summary.sample_count = 4
      summary_metric_from_cluster.summary.sample_sum = 94913
  
      # Create quantiles 0.5, 0.9, 0.99
      quantile_05 = summary_metric_from_cluster.summary.quantile.add()
      quantile_05.quantile = 0.5
      quantile_05.value = 24615
  
      quantile_09 = summary_metric_from_cluster.summary.quantile.add()
      quantile_09.quantile = 0.9
      quantile_09.value = 24627
  
      quantile_099 = summary_metric_from_cluster.summary.quantile.add()
      quantile_099.quantile = 0.99
      quantile_099.value = 24627
  
      # Iter on the generator to get all metrics
      response = MockResponse(text_data, 'text/plain; version=0.0.4')
      check = p_check
      metrics = [k for k in check.parse_metric_family(response)]
  
      assert 1 == len(metrics)
  
      current_metric = metrics[0]
      # in metrics with more than one label
      # the labels don't always get parsed in a deterministic order
      # deconstruct the metric to ensure it's equal
      assert expected_etcd_metric.help == current_metric.help
      assert expected_etcd_metric.name == current_metric.name
      assert expected_etcd_metric.type == current_metric.type
      for idx in xrange(len(expected_etcd_metric.metric)):
          assert expected_etcd_metric.metric[idx].summary == current_metric.metric[idx].summary
          for label in expected_etcd_metric.metric[idx].label:
              assert label in current_metric.metric[idx].label
  
  
  def test_parse_one_summary_with_none_values(p_check):
      text_data = (
          '# HELP http_response_size_bytes The HTTP response sizes in bytes.\n'
          '# TYPE http_response_size_bytes summary\n'
          'http_response_size_bytes{handler="prometheus",quantile="0.5"} NaN\n'
          'http_response_size_bytes{handler="prometheus",quantile="0.9"} NaN\n'
          'http_response_size_bytes{handler="prometheus",quantile="0.99"} NaN\n'
          'http_response_size_bytes_sum{handler="prometheus"} 0\n'
          'http_response_size_bytes_count{handler="prometheus"} 0\n')
  
      expected_etcd_metric = metrics_pb2.MetricFamily()
      expected_etcd_metric.help = "The HTTP response sizes in bytes."
      expected_etcd_metric.name = "http_response_size_bytes"
      expected_etcd_metric.type = 2
  
      summary_metric = expected_etcd_metric.metric.add()
  
      # Label for prometheus handler
      summary_label = summary_metric.label.add()
      summary_label.name, summary_label.value = "handler", "prometheus"
  
      # Root summary sample
      summary_metric.summary.sample_count = 0
      summary_metric.summary.sample_sum = 0.
  
      # Create quantiles 0.5, 0.9, 0.99
      quantile_05 = summary_metric.summary.quantile.add()
      quantile_05.quantile = 0.5
      quantile_05.value = float('nan')
  
      quantile_09 = summary_metric.summary.quantile.add()
      quantile_09.quantile = 0.9
      quantile_09.value = float('nan')
  
      quantile_099 = summary_metric.summary.quantile.add()
      quantile_099.quantile = 0.99
      quantile_099.value = float('nan')
  
      # Iter on the generator to get all metrics
      response = MockResponse(text_data, 'text/plain; version=0.0.4')
      check = p_check
      metrics = [k for k in check.parse_metric_family(response)]
      assert 1 == len(metrics)
      current_metric = metrics[0]
      # As the NaN value isn't supported when we are calling assertEqual
      # we need to compare the object representation instead of the object itself
      assert expected_etcd_metric.__repr__() == current_metric.__repr__()
  
  
  def test_label_joins(sorted_tags_check):
      """ Tests label join on text format """
      text_data = None
      f_name = os.path.join(os.path.dirname(__file__), 'fixtures', 'prometheus', 'ksm.txt')
      with open(f_name, 'r') as f:
          text_data = f.read()
      mock_response = mock.MagicMock(
          status_code=200,
          iter_lines=lambda **kwargs: text_data.split("\n"),
          headers={'Content-Type': "text/plain"})
      p = mock.patch('requests.get', return_value=mock_response, __name__="get")
      p.start()
      check = sorted_tags_check
      check.NAMESPACE = 'ksm'
      check.label_joins = {
          'kube_pod_info': {
              'label_to_match': 'pod',
              'labels_to_get': ['node', 'pod_ip']
          },
          'kube_deployment_labels': {
              'label_to_match': 'deployment',
              'labels_to_get': ['label_addonmanager_kubernetes_io_mode', 'label_k8s_app', 'label_kubernetes_io_cluster_service']
          }
      }
  
      check.metrics_mapper = {'kube_pod_status_ready': 'pod.ready',
                              'kube_pod_status_scheduled': 'pod.scheduled',
                              'kube_deployment_status_replicas': 'deploy.replicas.available'}
  
      check.gauge = mock.MagicMock()
      # dry run to build mapping
      check.process("http://fake.endpoint:10055/metrics")
      # run with submit
      check.process("http://fake.endpoint:10055/metrics")
  
      # check a bunch of metrics
      check.gauge.assert_has_calls([
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:event-exporter-v0.1.7-958884745-qgnbw',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-0kch',
                  'pod_ip:11.32.3.14']), hostname=None),
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-6dj58',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-0kch',
                  'pod_ip:11.132.0.7']), hostname=None),
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-z348z',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-j75z',
                  'pod_ip:11.132.0.14']), hostname=None),
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:heapster-v1.4.3-2027615481-lmjm5',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-j75z',
                  'pod_ip:11.32.5.7']), hostname=None),
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:kube-dns-3092422022-lvrmx',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-0kch',
                  'pod_ip:11.32.3.10']), hostname=None),
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:kube-dns-3092422022-x0tjx',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-0kch',
                  'pod_ip:11.32.3.9']), hostname=None),
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:kube-dns-autoscaler-97162954-mf6d3',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-j75z',
                  'pod_ip:11.32.5.6']), hostname=None),
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:kube-proxy-gke-foobar-test-kube-default-pool-9b4ff111-0kch',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-0kch',
                  'pod_ip:11.132.0.7']), hostname=None),
          mock.call('ksm.pod.scheduled', 1.0,
              sorted(['pod:ungaged-panther-kube-state-metrics-3918010230-64xwc',
                  'namespace:default',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-j75z',
                  'pod_ip:11.32.5.45']), hostname=None),
          mock.call('ksm.pod.scheduled', 1.0,
              sorted(['pod:event-exporter-v0.1.7-958884745-qgnbw',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-0kch',
                  'pod_ip:11.32.3.14']), hostname=None),
          mock.call('ksm.pod.scheduled', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-6dj58',
              'namespace:kube-system',
              'condition:true',
              'node:gke-foobar-test-kube-default-pool-9b4ff111-0kch',
              'pod_ip:11.132.0.7']), hostname=None),
          mock.call('ksm.pod.scheduled', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-z348z',
              'namespace:kube-system',
              'condition:true',
              'node:gke-foobar-test-kube-default-pool-9b4ff111-j75z',
              'pod_ip:11.132.0.14']), hostname=None),
          mock.call('ksm.pod.scheduled', 1.0,
              sorted(['pod:heapster-v1.4.3-2027615481-lmjm5',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-j75z',
                  'pod_ip:11.32.5.7']), hostname=None),
          mock.call('ksm.pod.scheduled', 1.0,
              sorted(['pod:kube-dns-3092422022-lvrmx',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-0kch',
                  'pod_ip:11.32.3.10']), hostname=None),
          mock.call('ksm.pod.scheduled', 1.0,
              sorted(['pod:kube-dns-3092422022-x0tjx',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-0kch',
                  'pod_ip:11.32.3.9']), hostname=None),
          mock.call('ksm.deploy.replicas.available', 1.0,
              sorted(['namespace:kube-system',
                  'deployment:event-exporter-v0.1.7',
                  'label_k8s_app:event-exporter',
                  'label_addonmanager_kubernetes_io_mode:Reconcile',
                  'label_kubernetes_io_cluster_service:true']), hostname=None),
          mock.call('ksm.deploy.replicas.available', 1.0,
              sorted(['namespace:kube-system',
                  'deployment:heapster-v1.4.3',
                  'label_k8s_app:heapster',
                  'label_addonmanager_kubernetes_io_mode:Reconcile',
                  'label_kubernetes_io_cluster_service:true']), hostname=None),
          mock.call('ksm.deploy.replicas.available', 2.0,
              sorted(['namespace:kube-system',
                  'deployment:kube-dns',
                  'label_kubernetes_io_cluster_service:true',
                  'label_addonmanager_kubernetes_io_mode:Reconcile',
                  'label_k8s_app:kube-dns']), hostname=None),
          mock.call('ksm.deploy.replicas.available', 1.0,
              sorted(['namespace:kube-system',
                  'deployment:kube-dns-autoscaler',
                  'label_kubernetes_io_cluster_service:true',
                  'label_addonmanager_kubernetes_io_mode:Reconcile',
                  'label_k8s_app:kube-dns-autoscaler']), hostname=None),
          mock.call('ksm.deploy.replicas.available', 1.0,
              sorted(['namespace:kube-system',
                  'deployment:kubernetes-dashboard',
                  'label_kubernetes_io_cluster_service:true',
                  'label_addonmanager_kubernetes_io_mode:Reconcile',
                  'label_k8s_app:kubernetes-dashboard']), hostname=None),
          mock.call('ksm.deploy.replicas.available', 1.0,
              sorted(['namespace:kube-system',
                  'deployment:l7-default-backend',
                  'label_k8s_app:glbc',
                  'label_addonmanager_kubernetes_io_mode:Reconcile',
                  'label_kubernetes_io_cluster_service:true']), hostname=None),
          mock.call('ksm.deploy.replicas.available', 1.0,
              sorted(['namespace:kube-system',
                  'deployment:tiller-deploy']), hostname=None),
          mock.call('ksm.deploy.replicas.available', 1.0,
              sorted(['namespace:default',
                  'deployment:ungaged-panther-kube-state-metrics']), hostname=None)
      ], any_order=True)
      p.stop()
  
  
  def test_label_joins_gc(sorted_tags_check):
      """ Tests label join GC on text format """
      text_data = None
      f_name = os.path.join(os.path.dirname(__file__), 'fixtures', 'prometheus', 'ksm.txt')
      with open(f_name, 'r') as f:
          text_data = f.read()
      mock_response = mock.MagicMock(
          status_code=200,
          iter_lines=lambda **kwargs: text_data.split("\n"),
          headers={'Content-Type': "text/plain"})
      p = mock.patch('requests.get', return_value=mock_response, __name__="get")
      p.start()
      check = sorted_tags_check
      check.NAMESPACE = 'ksm'
      check.label_joins = {
          'kube_pod_info': {
              'label_to_match': 'pod',
              'labels_to_get': ['node', 'pod_ip']
          }
      }
      check.metrics_mapper = {'kube_pod_status_ready': 'pod.ready'}
      check.gauge = mock.MagicMock()
      # dry run to build mapping
      check.process("http://fake.endpoint:10055/metrics")
      # run with submit
      check.process("http://fake.endpoint:10055/metrics")
      # check a bunch of metrics
      check.gauge.assert_has_calls([
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-6dj58',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-0kch',
                  'pod_ip:11.132.0.7']), hostname=None),
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-z348z',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-j75z',
                  'pod_ip:11.132.0.14']), hostname=None),
      ], any_order=True)
      assert 15 == len(check._label_mapping['pod'])
      text_data = text_data.replace('dd-agent-62bgh', 'dd-agent-1337')
      p.stop()
      mock_response = mock.MagicMock(
          status_code=200,
          iter_lines=lambda **kwargs: text_data.split("\n"),
          headers={'Content-Type': "text/plain"})
      p = mock.patch('requests.get', return_value=mock_response, __name__="get")
      p.start()
      check.process("http://fake.endpoint:10055/metrics")
      assert 'dd-agent-1337' in check._label_mapping['pod']
      assert 'dd-agent-62bgh' not in check._label_mapping['pod']
      assert 15 == len(check._label_mapping['pod'])
      p.stop()
  
  
  def test_label_joins_missconfigured(sorted_tags_check):
      """ Tests label join missconfigured label is ignored """
      text_data = None
      f_name = os.path.join(os.path.dirname(__file__), 'fixtures', 'prometheus', 'ksm.txt')
      with open(f_name, 'r') as f:
          text_data = f.read()
      mock_response = mock.MagicMock(
          status_code=200,
          iter_lines=lambda **kwargs: text_data.split("\n"),
          headers={'Content-Type': "text/plain"})
      p = mock.patch('requests.get', return_value=mock_response, __name__="get")
      p.start()
      check = sorted_tags_check
      check.NAMESPACE = 'ksm'
      check.label_joins = {
          'kube_pod_info': {
              'label_to_match': 'pod',
              'labels_to_get': ['node', 'not_existing']
          }
      }
      check.metrics_mapper = {'kube_pod_status_ready': 'pod.ready'}
      check.gauge = mock.MagicMock()
      # dry run to build mapping
      check.process("http://fake.endpoint:10055/metrics")
      # run with submit
      check.process("http://fake.endpoint:10055/metrics")
      # check a bunch of metrics
      check.gauge.assert_has_calls([
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-6dj58',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-0kch']), hostname=None),
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-z348z',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-j75z']), hostname=None),
      ], any_order=True)
      p.stop()
  
  
  def test_label_join_not_existing(sorted_tags_check):
      """ Tests label join on non existing matching label is ignored """
      text_data = None
      f_name = os.path.join(os.path.dirname(__file__), 'fixtures', 'prometheus', 'ksm.txt')
      with open(f_name, 'r') as f:
          text_data = f.read()
      mock_response = mock.MagicMock(
          status_code=200,
          iter_lines=lambda **kwargs: text_data.split("\n"),
          headers={'Content-Type': "text/plain"})
      p = mock.patch('requests.get', return_value=mock_response, __name__="get")
      p.start()
      check = sorted_tags_check
      check.NAMESPACE = 'ksm'
      check.label_joins = {
          'kube_pod_info': {
              'label_to_match': 'not_existing',
              'labels_to_get': ['node', 'pod_ip']
          }
      }
      check.metrics_mapper = {'kube_pod_status_ready': 'pod.ready'}
      check.gauge = mock.MagicMock()
      # dry run to build mapping
      check.process("http://fake.endpoint:10055/metrics")
      # run with submit
      check.process("http://fake.endpoint:10055/metrics")
      # check a bunch of metrics
      check.gauge.assert_has_calls([
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-6dj58',
                  'namespace:kube-system',
                  'condition:true']), hostname=None),
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-z348z',
                  'namespace:kube-system',
                  'condition:true']), hostname=None),
      ], any_order=True)
      p.stop()
  
  
  def test_label_join_metric_not_existing(sorted_tags_check):
      """ Tests label join on non existing metric is ignored """
      text_data = None
      f_name = os.path.join(os.path.dirname(__file__), 'fixtures', 'prometheus', 'ksm.txt')
      with open(f_name, 'r') as f:
          text_data = f.read()
      mock_response = mock.MagicMock(
          status_code=200,
          iter_lines=lambda **kwargs: text_data.split("\n"),
          headers={'Content-Type': "text/plain"})
      p = mock.patch('requests.get', return_value=mock_response, __name__="get")
      p.start()
      check = sorted_tags_check
      check.NAMESPACE = 'ksm'
      check.label_joins = {
          'not_existing': {
              'label_to_match': 'pod',
              'labels_to_get': ['node', 'pod_ip']
          }
      }
      check.metrics_mapper = {'kube_pod_status_ready': 'pod.ready'}
      check.gauge = mock.MagicMock()
      # dry run to build mapping
      check.process("http://fake.endpoint:10055/metrics")
      # run with submit
      check.process("http://fake.endpoint:10055/metrics")
      # check a bunch of metrics
      check.gauge.assert_has_calls([
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-6dj58',
                  'namespace:kube-system',
                  'condition:true']), hostname=None),
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-z348z',
                  'namespace:kube-system',
                  'condition:true']), hostname=None),
      ], any_order=True)
      p.stop()
  
  
  def test_label_join_with_hostname(sorted_tags_check):
      """ Tests label join and hostname override on a metric """
      text_data = None
      f_name = os.path.join(os.path.dirname(__file__), 'fixtures', 'prometheus', 'ksm.txt')
      with open(f_name, 'r') as f:
          text_data = f.read()
      mock_response = mock.MagicMock(
          status_code=200,
          iter_lines=lambda **kwargs: text_data.split("\n"),
          headers={'Content-Type': "text/plain"})
      p = mock.patch('requests.get', return_value=mock_response, __name__="get")
      p.start()
      check = sorted_tags_check
      check.NAMESPACE = 'ksm'
      check.label_joins = {
          'kube_pod_info': {
              'label_to_match': 'pod',
              'labels_to_get': ['node']
          }
      }
      check.label_to_hostname = 'node'
      check.metrics_mapper = {'kube_pod_status_ready': 'pod.ready'}
      check.gauge = mock.MagicMock()
      # dry run to build mapping
      check.process("http://fake.endpoint:10055/metrics")
      # run with submit
      check.process("http://fake.endpoint:10055/metrics")
      # check a bunch of metrics
      check.gauge.assert_has_calls([
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-6dj58',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-0kch']), hostname='gke-foobar-test-kube-default-pool-9b4ff111-0kch'),
          mock.call('ksm.pod.ready', 1.0,
              sorted(['pod:fluentd-gcp-v2.0.9-z348z',
                  'namespace:kube-system',
                  'condition:true',
                  'node:gke-foobar-test-kube-default-pool-9b4ff111-j75z']), hostname='gke-foobar-test-kube-default-pool-9b4ff111-j75z'),
      ], any_order=True)
      p.stop()
  
  @pytest.fixture()
  def mock_get():
      text_data = None
      f_name = os.path.join(os.path.dirname(__file__), 'fixtures', 'prometheus', 'ksm.txt')
      with open(f_name, 'r') as f:
          text_data = f.read()
      mock_get = mock.patch(
          'requests.get',
          return_value=mock.MagicMock(
              status_code=200,
              iter_lines=lambda **kwargs: text_data.split("\n"),
              headers={'Content-Type': "text/plain"}
          )
      )
  
      yield mock_get.start()
  
      mock_get.stop()
  
  
  def test_health_service_check_ok(mock_get):
      """ Tests endpoint health service check OK """
      check = PrometheusCheck('prometheus_check', {}, {}, {})
      check.NAMESPACE = 'ksm'
      check.health_service_check = True
      check.service_check = mock.MagicMock()
      check.process("http://fake.endpoint:10055/metrics")
      check.service_check.assert_called_with(
          "ksm.prometheus.health",
          PrometheusCheck.OK,
          tags=["endpoint:http://fake.endpoint:10055/metrics"]
      )
  
  def test_health_service_check_failing():
      """ Tests endpoint health service check failing """
      check = PrometheusCheck('prometheus_check', {}, {}, {})
      check.NAMESPACE = 'ksm'
      check.health_service_check = True
      check.service_check = mock.MagicMock()
      with pytest.raises(requests.ConnectionError):
          check.process("http://fake.endpoint:10055/metrics")
      check.service_check.assert_called_with(
          "ksm.prometheus.health",
          PrometheusCheck.CRITICAL,
          tags=["endpoint:http://fake.endpoint:10055/metrics"]
      )
  
  def test_set_prometheus_timeout():
      """ Tests set_prometheus_timeout function call from a PrometheusCheck"""
      # no timeout specified, should be default 10
      check = PrometheusCheck('prometheus_check', {}, {}, {})
      instance_default = {
          'prometheus_url': 'http://localhost:10249/metrics',
          'namespace': 'foobar',
          'metrics': [
              'metric3'
          ]
      }
      check.set_prometheus_timeout(instance_default)
      assert check.prometheus_timeout == 10
  
      # timeout set to 3
      check2 = PrometheusCheck('prometheus_check', {}, {}, {})
      instance_timeout_set = {
          'prometheus_timeout': 3,
          'prometheus_url': 'http://localhost:10249/metrics',
          'namespace': 'foobar',
          'metrics': [
              'metric3'
          ]
      }
      check2.set_prometheus_timeout(instance_timeout_set)
      assert check2.prometheus_timeout == 3
  