import math
import unittest

from prometheus_client.core import (
    BucketSpan, CollectorRegistry, CounterMetricFamily, Exemplar,
    GaugeHistogramMetricFamily, GaugeMetricFamily, HistogramMetricFamily,
    InfoMetricFamily, Metric, NativeHistogram, Sample, StateSetMetricFamily,
    SummaryMetricFamily, Timestamp,
)
from prometheus_client.openmetrics.exposition import generate_latest
from prometheus_client.openmetrics.parser import text_string_to_metric_families


class TestParse(unittest.TestCase):

    def test_simple_counter(self):
        families = text_string_to_metric_families("""# TYPE a counter
# HELP a help
a_total 1
# EOF
""")
        self.assertEqual([CounterMetricFamily("a", "help", value=1)], list(families))

    def test_uint64_counter(self):
        families = text_string_to_metric_families("""# TYPE a counter
# HELP a help
a_total 9223372036854775808
# EOF
""")
        self.assertEqual([CounterMetricFamily("a", "help", value=9223372036854775808)], list(families))

    def test_utf8_counter(self):
        families = text_string_to_metric_families("""# TYPE "my.counter" counter
# HELP "my.counter" help
{"my.counter_total"} 1
# EOF
""")
        self.assertEqual([CounterMetricFamily("my.counter", "help", value=1)], list(families))

    def test_complex_name_counter(self):
        families = text_string_to_metric_families("""# TYPE "my.counter{} # = \\" \\n" counter
# HELP "my.counter{} # = \\" \\n" help
{"my.counter{} # = \\" \\n_total", "awful. }}{{ # HELP EOF name"="\\n yikes } \\" value"} 1
# EOF
""")
        metric = CounterMetricFamily("my.counter{} # = \" \n", "help", labels={'awful. }}{{ # HELP EOF name': '\n yikes } " value'})
        metric.add_sample("my.counter{} # = \" \n_total", {'awful. }}{{ # HELP EOF name': '\n yikes } " value'}, 1)
        self.assertEqual([metric], list(families))

    def test_simple_gauge(self):
        families = text_string_to_metric_families("""# TYPE a gauge
# HELP a help
a 1
# EOF
""")
        self.assertEqual([GaugeMetricFamily("a", "help", value=1)], list(families))

    def test_float_gauge(self):
        families = text_string_to_metric_families("""# TYPE a gauge
# HELP a help
a 1.2
# EOF
""")
        self.assertEqual([GaugeMetricFamily("a", "help", value=1.2)], list(families))

    def test_leading_zeros_simple_gauge(self):
        families = text_string_to_metric_families("""# TYPE a gauge
# HELP a help
a 0000000000000000000000000000000000000000001
# EOF
""")
        self.assertEqual([GaugeMetricFamily("a", "help", value=1)], list(families))

    def test_leading_zeros_float_gauge(self):
        families = text_string_to_metric_families("""# TYPE a gauge
# HELP a help
a 0000000000000000000000000000000000000000001.2e-1
# EOF
""")
        self.assertEqual([GaugeMetricFamily("a", "help", value=.12)], list(families))

    def test_nan_gauge(self):
        families = text_string_to_metric_families("""# TYPE a gauge
# HELP a help
a NaN
# EOF
""")
        self.assertTrue(math.isnan(list(families)[0].samples[0].value))

    def test_unit_gauge(self):
        families = text_string_to_metric_families("""# TYPE a_seconds gauge
# UNIT a_seconds seconds
# HELP a_seconds help
a_seconds 1
# EOF
""")
        self.assertEqual([GaugeMetricFamily("a_seconds", "help", value=1, unit='seconds')], list(families))

    def test_simple_summary(self):
        families = text_string_to_metric_families("""# TYPE a summary
# HELP a help
a_count 1
a_sum 2
# EOF
""")
        summary = SummaryMetricFamily("a", "help", count_value=1, sum_value=2)
        self.assertEqual([summary], list(families))

    def test_summary_quantiles(self):
        families = text_string_to_metric_families("""# TYPE a summary
# HELP a help
a_count 1
a_sum 2
a{quantile="0.5"} 0.7
a{quantile="1"} 0.8
# EOF
""")
        # The Python client doesn't support quantiles, but we
        # still need to be able to parse them.
        metric_family = SummaryMetricFamily("a", "help", count_value=1, sum_value=2)
        metric_family.add_sample("a", {"quantile": "0.5"}, 0.7)
        metric_family.add_sample("a", {"quantile": "1"}, 0.8)
        self.assertEqual([metric_family], list(families))

    def test_simple_histogram(self):
        families = text_string_to_metric_families("""# TYPE a histogram
# HELP a help
a_bucket{le="1.0"} 0
a_bucket{le="+Inf"} 3
a_count 3
a_sum 2
# EOF
""")
        self.assertEqual([HistogramMetricFamily("a", "help", sum_value=2, buckets=[("1.0", 0.0), ("+Inf", 3.0)])],
                         list(families))

    def test_simple_histogram_float_values(self):
        families = text_string_to_metric_families("""# TYPE a histogram
# HELP a help
a_bucket{le="1.0"} 0.0
a_bucket{le="+Inf"} 3.0
a_count 3.0
a_sum 2.0
# EOF
""")
        self.assertEqual([HistogramMetricFamily("a", "help", sum_value=2, buckets=[("1.0", 0.0), ("+Inf", 3.0)])],
                         list(families))

    def test_utf8_histogram_float_values(self):
        families = text_string_to_metric_families("""# TYPE "a.b" histogram
# HELP "a.b" help
{"a.b_bucket", le="1.0"} 0.0
{"a.b_bucket", le="+Inf"} 3.0
{"a.b_count"} 3.0
{"a.b_sum"} 2.0
# EOF
""")
        self.assertEqual([HistogramMetricFamily("a.b", "help", sum_value=2, buckets=[("1.0", 0.0), ("+Inf", 3.0)])],
                         list(families))

    def test_histogram_noncanonical(self):
        families = text_string_to_metric_families("""# TYPE a histogram
# HELP a help
a_bucket{le="0"} 0
a_bucket{le="0.00000000001"} 0
a_bucket{le="0.0000000001"} 0
a_bucket{le="1e-04"} 0
a_bucket{le="1.1e-4"} 0
a_bucket{le="1.1e-3"} 0
a_bucket{le="1.1e-2"} 0
a_bucket{le="1"} 0
a_bucket{le="1e+05"} 0
a_bucket{le="10000000000"} 0
a_bucket{le="100000000000.0"} 0
a_bucket{le="+Inf"} 3
a_count 3
a_sum 2
# EOF
""")
        list(families)

    def test_negative_bucket_histogram(self):
        families = text_string_to_metric_families("""# TYPE a histogram
# HELP a help
a_bucket{le="-1.0"} 0
a_bucket{le="1.0"} 1
a_bucket{le="+Inf"} 3
# EOF
""")
        self.assertEqual([HistogramMetricFamily("a", "help", buckets=[("-1.0", 0.0), ("1.0", 1.0), ("+Inf", 3.0)])],
                         list(families))

    def test_histogram_exemplars(self):
        families = text_string_to_metric_families("""# TYPE a histogram
# HELP a help
a_bucket{le="1.0"} 0 # {a="b"} 0.5
a_bucket{le="2.0"} 2 # {a="c"} 0.5
a_bucket{le="+Inf"} 3 # {a="2345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"} 4 123
# EOF
""")
        hfm = HistogramMetricFamily("a", "help")
        hfm.add_sample("a_bucket", {"le": "1.0"}, 0.0, None, Exemplar({"a": "b"}, 0.5))
        hfm.add_sample("a_bucket", {"le": "2.0"}, 2.0, None, Exemplar({"a": "c"}, 0.5)),
        hfm.add_sample("a_bucket", {"le": "+Inf"}, 3.0, None,
                       Exemplar({"a": "2345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678"}, 4,
                                Timestamp(123, 0)))
        self.assertEqual([hfm], list(families))

    def test_native_histogram(self):
        families = text_string_to_metric_families("""# TYPE nativehistogram histogram
# HELP nativehistogram Is a basic example of a native histogram
nativehistogram {count:24,sum:100,schema:0,zero_threshold:0.001,zero_count:4,positive_spans:[0:2,1:2],negative_spans:[0:2,1:2],positive_deltas:[2,1,-3,3],negative_deltas:[2,1,-2,3]}
# EOF
""")
        families = list(families)

        hfm = HistogramMetricFamily("nativehistogram", "Is a basic example of a native histogram")
        hfm.add_sample("nativehistogram", None, None, None, None, NativeHistogram(24, 100, 0, 0.001, 4, (BucketSpan(0, 2), BucketSpan(1, 2)), (BucketSpan(0, 2), BucketSpan(1, 2)), (2, 1, -3, 3), (2, 1, -2, 3)))
        self.assertEqual([hfm], families)

    def test_native_histogram_utf8(self):
        families = text_string_to_metric_families("""# TYPE "native{histogram" histogram
# HELP "native{histogram" Is a basic example of a native histogram
{"native{histogram"} {count:24,sum:100,schema:0,zero_threshold:0.001,zero_count:4,positive_spans:[0:2,1:2],negative_spans:[0:2,1:2],positive_deltas:[2,1,-3,3],negative_deltas:[2,1,-2,3]}
# EOF
""")
        families = list(families)

        hfm = HistogramMetricFamily("native{histogram", "Is a basic example of a native histogram")
        hfm.add_sample("native{histogram", None, None, None, None, NativeHistogram(24, 100, 0, 0.001, 4, (BucketSpan(0, 2), BucketSpan(1, 2)), (BucketSpan(0, 2), BucketSpan(1, 2)), (2, 1, -3, 3), (2, 1, -2, 3)))
        self.assertEqual([hfm], families)

    def test_native_histogram_utf8_stress(self):
        families = text_string_to_metric_families("""# TYPE "native{histogram" histogram
# HELP "native{histogram" Is a basic example of a native histogram
{"native{histogram", "xx{} # {}"=" EOF # {}}}"} {count:24,sum:100,schema:0,zero_threshold:0.001,zero_count:4,positive_spans:[0:2,1:2],negative_spans:[0:2,1:2],positive_deltas:[2,1,-3,3],negative_deltas:[2,1,-2,3]}
# EOF
""")
        families = list(families)

        hfm = HistogramMetricFamily("native{histogram", "Is a basic example of a native histogram")
        hfm.add_sample("native{histogram", {'xx{} # {}': ' EOF # {}}}'}, None, None, None, NativeHistogram(24, 100, 0, 0.001, 4, (BucketSpan(0, 2), BucketSpan(1, 2)), (BucketSpan(0, 2), BucketSpan(1, 2)), (2, 1, -3, 3), (2, 1, -2, 3)))
        self.assertEqual([hfm], families)

    def test_native_histogram_three_pos_spans_no_neg_spans_or_deltas(self):
        families = text_string_to_metric_families("""# TYPE nhsp histogram
# HELP nhsp Is a basic example of a native histogram with three spans
nhsp {count:4,sum:6,schema:3,zero_threshold:2.938735877055719e-39,zero_count:1,positive_spans:[0:1,7:1,4:1],positive_deltas:[1,0,0]}
# EOF
""")
        families = list(families)
       
        hfm = HistogramMetricFamily("nhsp", "Is a basic example of a native histogram with three spans")
        hfm.add_sample("nhsp", None, None, None, None, NativeHistogram(4, 6, 3, 2.938735877055719e-39, 1, (BucketSpan(0, 1), BucketSpan(7, 1), BucketSpan(4, 1)), None, (1, 0, 0), None))
        self.assertEqual([hfm], families)

    def test_native_histogram_with_labels(self):
        families = text_string_to_metric_families("""# TYPE hist_w_labels histogram
# HELP hist_w_labels Is a basic example of a native histogram with labels
hist_w_labels{foo="bar",baz="qux"} {count:24,sum:100,schema:0,zero_threshold:0.001,zero_count:4,positive_spans:[0:2,1:2],negative_spans:[0:2,1:2],positive_deltas:[2,1,-3,3],negative_deltas:[2,1,-2,3]}
# EOF
""")
        families = list(families)

        hfm = HistogramMetricFamily("hist_w_labels", "Is a basic example of a native histogram with labels")
        hfm.add_sample("hist_w_labels", {"foo": "bar", "baz": "qux"}, None, None, None, NativeHistogram(24, 100, 0, 0.001, 4, (BucketSpan(0, 2), BucketSpan(1, 2)), (BucketSpan(0, 2), BucketSpan(1, 2)), (2, 1, -3, 3), (2, 1, -2, 3)))
        self.assertEqual([hfm], families)

    def test_native_histogram_with_labels_utf8(self):
        families = text_string_to_metric_families("""# TYPE "hist.w.labels" histogram
# HELP "hist.w.labels" Is a basic example of a native histogram with labels
{"hist.w.labels", foo="bar",baz="qux"} {count:24,sum:100,schema:0,zero_threshold:0.001,zero_count:4,positive_spans:[0:2,1:2],negative_spans:[0:2,1:2],positive_deltas:[2,1,-3,3],negative_deltas:[2,1,-2,3]}
# EOF
""")
        families = list(families)

        hfm = HistogramMetricFamily("hist.w.labels", "Is a basic example of a native histogram with labels")
        hfm.add_sample("hist.w.labels", {"foo": "bar", "baz": "qux"}, None, None, None, NativeHistogram(24, 100, 0, 0.001, 4, (BucketSpan(0, 2), BucketSpan(1, 2)), (BucketSpan(0, 2), BucketSpan(1, 2)), (2, 1, -3, 3), (2, 1, -2, 3)))
        self.assertEqual([hfm], families)

    def test_native_histogram_with_classic_histogram(self):
        families = text_string_to_metric_families("""# TYPE hist_w_classic histogram
# HELP hist_w_classic Is a basic example of a native histogram coexisting with a classic histogram
hist_w_classic{foo="bar"} {count:24,sum:100,schema:0,zero_threshold:0.001,zero_count:4,positive_spans:[0:2,1:2],negative_spans:[0:2,1:2],positive_deltas:[2,1,-3,3],negative_deltas:[2,1,-2,3]}
hist_w_classic_bucket{foo="bar",le="0.001"} 4
hist_w_classic_bucket{foo="bar",le="+Inf"} 24
hist_w_classic_count{foo="bar"} 24
hist_w_classic_sum{foo="bar"} 100
# EOF
""")
        families = list(families)

        hfm = HistogramMetricFamily("hist_w_classic", "Is a basic example of a native histogram coexisting with a classic histogram")
        hfm.add_sample("hist_w_classic", {"foo": "bar"}, None, None, None, NativeHistogram(24, 100, 0, 0.001, 4, (BucketSpan(0, 2), BucketSpan(1, 2)), (BucketSpan(0, 2), BucketSpan(1, 2)), (2, 1, -3, 3), (2, 1, -2, 3)))
        hfm.add_sample("hist_w_classic_bucket", {"foo": "bar", "le": "0.001"}, 4.0, None, None, None)
        hfm.add_sample("hist_w_classic_bucket", {"foo": "bar", "le": "+Inf"}, 24.0, None, None, None)
        hfm.add_sample("hist_w_classic_count", {"foo": "bar"}, 24.0, None, None, None)
        hfm.add_sample("hist_w_classic_sum", {"foo": "bar"}, 100.0, None, None, None)
        self.assertEqual([hfm], families)

    def test_native_plus_classic_histogram_two_labelsets(self):
        families = text_string_to_metric_families("""# TYPE hist_w_classic_two_sets histogram
# HELP hist_w_classic_two_sets Is an example of a native histogram plus a classic histogram with two label sets
hist_w_classic_two_sets{foo="bar"} {count:24,sum:100,schema:0,zero_threshold:0.001,zero_count:4,positive_spans:[0:2,1:2],negative_spans:[0:2,1:2],positive_deltas:[2,1,-3,3],negative_deltas:[2,1,-2,3]}
hist_w_classic_two_sets_bucket{foo="bar",le="0.001"} 4
hist_w_classic_two_sets_bucket{foo="bar",le="+Inf"} 24
hist_w_classic_two_sets_count{foo="bar"} 24
hist_w_classic_two_sets_sum{foo="bar"} 100
hist_w_classic_two_sets{foo="baz"} {count:24,sum:100,schema:0,zero_threshold:0.001,zero_count:4,positive_spans:[0:2,1:2],negative_spans:[0:2,1:2],positive_deltas:[2,1,-3,3],negative_deltas:[2,1,-2,3]}
hist_w_classic_two_sets_bucket{foo="baz",le="0.001"} 4
hist_w_classic_two_sets_bucket{foo="baz",le="+Inf"} 24
hist_w_classic_two_sets_count{foo="baz"} 24
hist_w_classic_two_sets_sum{foo="baz"} 100
# EOF
""")
        families = list(families)

        hfm = HistogramMetricFamily("hist_w_classic_two_sets", "Is an example of a native histogram plus a classic histogram with two label sets")
        hfm.add_sample("hist_w_classic_two_sets", {"foo": "bar"}, None, None, None, NativeHistogram(24, 100, 0, 0.001, 4, (BucketSpan(0, 2), BucketSpan(1, 2)), (BucketSpan(0, 2), BucketSpan(1, 2)), (2, 1, -3, 3), (2, 1, -2, 3)))
        hfm.add_sample("hist_w_classic_two_sets_bucket", {"foo": "bar", "le": "0.001"}, 4.0, None, None, None)
        hfm.add_sample("hist_w_classic_two_sets_bucket", {"foo": "bar", "le": "+Inf"}, 24.0, None, None, None)
        hfm.add_sample("hist_w_classic_two_sets_count", {"foo": "bar"}, 24.0, None, None, None)
        hfm.add_sample("hist_w_classic_two_sets_sum", {"foo": "bar"}, 100.0, None, None, None)
        hfm.add_sample("hist_w_classic_two_sets", {"foo": "baz"}, None, None, None, NativeHistogram(24, 100, 0, 0.001, 4, (BucketSpan(0, 2), BucketSpan(1, 2)), (BucketSpan(0, 2), BucketSpan(1, 2)), (2, 1, -3, 3), (2, 1, -2, 3)))
        hfm.add_sample("hist_w_classic_two_sets_bucket", {"foo": "baz", "le": "0.001"}, 4.0, None, None, None)
        hfm.add_sample("hist_w_classic_two_sets_bucket", {"foo": "baz", "le": "+Inf"}, 24.0, None, None, None)
        hfm.add_sample("hist_w_classic_two_sets_count", {"foo": "baz"}, 24.0, None, None, None)
        hfm.add_sample("hist_w_classic_two_sets_sum", {"foo": "baz"}, 100.0, None, None, None)
        self.assertEqual([hfm], families)

    def test_simple_gaugehistogram(self):
        families = text_string_to_metric_families("""# TYPE a gaugehistogram
# HELP a help
a_bucket{le="1.0"} 0
a_bucket{le="+Inf"} 3
a_gcount 3
a_gsum 2
# EOF
""")
        self.assertEqual([GaugeHistogramMetricFamily("a", "help", gsum_value=2, buckets=[("1.0", 0.0), ("+Inf", 3.0)])],
                         list(families))

    def test_negative_bucket_gaugehistogram(self):
        families = text_string_to_metric_families("""# TYPE a gaugehistogram
# HELP a help
a_bucket{le="-1.0"} 1
a_bucket{le="1.0"} 2
a_bucket{le="+Inf"} 3
a_gcount 3
a_gsum -5
# EOF
""")
        self.assertEqual([GaugeHistogramMetricFamily("a", "help", gsum_value=-5, buckets=[("-1.0", 1.0), ("1.0", 2.0), ("+Inf", 3.0)])],
                         list(families))

    def test_gaugehistogram_exemplars(self):
        families = text_string_to_metric_families("""# TYPE a gaugehistogram
# HELP a help
a_bucket{le="1.0"} 0 123 # {a="b"} 0.5
a_bucket{le="2.0"} 2 123 # {a="c"} 0.5
a_bucket{le="+Inf"} 3 123 # {a="d"} 4 123
# EOF
""")
        hfm = GaugeHistogramMetricFamily("a", "help")
        hfm.add_sample("a_bucket", {"le": "1.0"}, 0.0, Timestamp(123, 0), Exemplar({"a": "b"}, 0.5))
        hfm.add_sample("a_bucket", {"le": "2.0"}, 2.0, Timestamp(123, 0), Exemplar({"a": "c"}, 0.5)),
        hfm.add_sample("a_bucket", {"le": "+Inf"}, 3.0, Timestamp(123, 0), Exemplar({"a": "d"}, 4, Timestamp(123, 0)))
        self.assertEqual([hfm], list(families))

    def test_counter_exemplars(self):
        families = text_string_to_metric_families("""# TYPE a counter
# HELP a help
a_total 0 123 # {a="b"} 0.5
# EOF
""")
        cfm = CounterMetricFamily("a", "help")
        cfm.add_sample("a_total", {}, 0.0, Timestamp(123, 0), Exemplar({"a": "b"}, 0.5))
        self.assertEqual([cfm], list(families))

    def test_counter_exemplars_utf8(self):
        families = text_string_to_metric_families("""# TYPE "a.b" counter
# HELP "a.b" help
{"a.b_total"} 0 123 # {"c{}d"="b"} 0.5
# EOF
""")
        cfm = CounterMetricFamily("a.b", "help")
        cfm.add_sample("a.b_total", {}, 0.0, Timestamp(123, 0), Exemplar({"c{}d": "b"}, 0.5))
        self.assertEqual([cfm], list(families))

    def test_counter_exemplars_empty_brackets(self):
        families = text_string_to_metric_families("""# TYPE a counter
# HELP a help
a_total{} 0 123 # {a="b"} 0.5
# EOF
""")
        cfm = CounterMetricFamily("a", "help")
        cfm.add_sample("a_total", {}, 0.0, Timestamp(123, 0), Exemplar({"a": "b"}, 0.5))
        self.assertEqual([cfm], list(families))

    def test_simple_info(self):
        families = text_string_to_metric_families("""# TYPE a info
# HELP a help
a_info{foo="bar"} 1
# EOF
""")
        self.assertEqual([InfoMetricFamily("a", "help", {'foo': 'bar'})], list(families))

    def test_info_timestamps(self):
        families = text_string_to_metric_families("""# TYPE a info
# HELP a help
a_info{a="1",foo="bar"} 1 1
a_info{a="2",foo="bar"} 1 0
# EOF
""")
        imf = InfoMetricFamily("a", "help")
        imf.add_sample("a_info", {"a": "1", "foo": "bar"}, 1, Timestamp(1, 0))
        imf.add_sample("a_info", {"a": "2", "foo": "bar"}, 1, Timestamp(0, 0))
        self.assertEqual([imf], list(families))

    def test_simple_stateset(self):
        families = text_string_to_metric_families("""# TYPE a stateset
# HELP a help
a{a="bar"} 0
a{a="foo"} 1.0
# EOF
""")
        self.assertEqual([StateSetMetricFamily("a", "help", {'foo': True, 'bar': False})], list(families))

    def test_duplicate_timestamps(self):
        families = text_string_to_metric_families("""# TYPE a gauge
# HELP a help
a{a="1",foo="bar"} 1 0.0000000000
a{a="1",foo="bar"} 2 0.0000000001
a{a="1",foo="bar"} 3 0.0000000010
a{a="2",foo="bar"} 4 0.0000000000
a{a="2",foo="bar"} 5 0.0000000001
# EOF
""")
        imf = GaugeMetricFamily("a", "help")
        imf.add_sample("a", {"a": "1", "foo": "bar"}, 1, Timestamp(0, 0))
        imf.add_sample("a", {"a": "1", "foo": "bar"}, 3, Timestamp(0, 1))
        imf.add_sample("a", {"a": "2", "foo": "bar"}, 4, Timestamp(0, 0))
        self.assertEqual([imf], list(families))

    def test_no_metadata(self):
        families = text_string_to_metric_families("""a 1
# EOF
""")
        metric_family = Metric("a", "", "untyped")
        metric_family.add_sample("a", {}, 1)
        self.assertEqual([metric_family], list(families))

    def test_empty_metadata(self):
        families = text_string_to_metric_families("""# HELP a 
# UNIT a 
# EOF
""")
        metric_family = Metric("a", "", "untyped")
        self.assertEqual([metric_family], list(families))

    def test_untyped(self):
        # https://github.com/prometheus/client_python/issues/79
        families = text_string_to_metric_families("""# HELP redis_connected_clients Redis connected clients
# TYPE redis_connected_clients unknown
redis_connected_clients{instance="rough-snowflake-web",port="6380"} 10.0
redis_connected_clients{instance="rough-snowflake-web",port="6381"} 12.0
# EOF
""")
        m = Metric("redis_connected_clients", "Redis connected clients", "untyped")
        m.samples = [
            Sample("redis_connected_clients", {"instance": "rough-snowflake-web", "port": "6380"}, 10),
            Sample("redis_connected_clients", {"instance": "rough-snowflake-web", "port": "6381"}, 12),
        ]
        self.assertEqual([m], list(families))

    def test_type_help_switched(self):
        families = text_string_to_metric_families("""# HELP a help
# TYPE a counter
a_total 1
# EOF
""")
        self.assertEqual([CounterMetricFamily("a", "help", value=1)], list(families))

    def test_labels_with_curly_braces(self):
        families = text_string_to_metric_families("""# TYPE a counter
# HELP a help
a_total{foo="bar",bar="b{a}z"} 1
# EOF
""")
        metric_family = CounterMetricFamily("a", "help", labels=["foo", "bar"])
        metric_family.add_metric(["bar", "b{a}z"], 1)
        self.assertEqual([metric_family], list(families))

    def test_empty_help(self):
        families = text_string_to_metric_families("""# TYPE a counter
# HELP a 
a_total 1
# EOF
""")
        self.assertEqual([CounterMetricFamily("a", "", value=1)], list(families))

    def test_labels_and_infinite(self):
        families = text_string_to_metric_families("""# TYPE a gauge
# HELP a help
a{foo="bar"} +Inf
a{foo="baz"} -Inf
# EOF
""")
        metric_family = GaugeMetricFamily("a", "help", labels=["foo"])
        metric_family.add_metric(["bar"], float('inf'))
        metric_family.add_metric(["baz"], float('-inf'))
        self.assertEqual([metric_family], list(families))

    def test_empty_brackets(self):
        families = text_string_to_metric_families("""# TYPE a counter
# HELP a help
a_total{} 1
# EOF
""")
        self.assertEqual([CounterMetricFamily("a", "help", value=1)], list(families))

    def test_nan(self):
        families = text_string_to_metric_families("""a NaN
# EOF
""")
        self.assertTrue(math.isnan(list(families)[0].samples[0][2]))

    def test_no_newline_after_eof(self):
        families = text_string_to_metric_families("""# TYPE a gauge
# HELP a help
a 1
# EOF""")
        self.assertEqual([GaugeMetricFamily("a", "help", value=1)], list(families))

    def test_empty_label(self):
        families = text_string_to_metric_families("""# TYPE a counter
# HELP a help
a_total{foo="bar"} 1
a_total{foo=""} 2
# EOF
""")
        metric_family = CounterMetricFamily("a", "help", labels=["foo"])
        metric_family.add_metric(["bar"], 1)
        metric_family.add_metric([""], 2)
        self.assertEqual([metric_family], list(families))

    def test_label_escaping(self):
        for escaped_val, unescaped_val in [('foo', 'foo'), ('\\foo', '\\foo'), ('\\\\foo', '\\foo'),
                                           ('foo\\\\', 'foo\\'), ('\\\\', '\\'), ('\\n', '\n'),
                                           ('\\\\n', '\\n'), ('\\\\\\n', '\\\n'), ('\\"', '"'), ('\\\\\\"', '\\"')]:
            families = list(text_string_to_metric_families("""# TYPE a counter
# HELP a help
a_total{foo="%s",bar="baz"} 1
# EOF
""" % escaped_val))
            metric_family = CounterMetricFamily(
                "a", "help", labels=["foo", "bar"])
            metric_family.add_metric([unescaped_val, "baz"], 1)
            self.assertEqual([metric_family], list(families))

    def test_help_escaping(self):
        for escaped_val, unescaped_val in [
                ('foo', 'foo'),
                ('\\foo', '\\foo'),
                ('\\\\foo', '\\foo'),
                ('foo\\', 'foo\\'),
                ('foo\\\\', 'foo\\'),
                ('\\n', '\n'),
                ('\\\\n', '\\n'),
                ('\\\\\\n', '\\\n'),
                ('\\"', '"'),
                ('\\\\"', '\\"'),
                ('\\\\\\"', '\\"')]:
            families = list(text_string_to_metric_families("""# TYPE a counter
# HELP a %s
a_total{foo="bar"} 1
# EOF
""" % escaped_val))
            metric_family = CounterMetricFamily("a", unescaped_val, labels=["foo"])
            metric_family.add_metric(["bar"], 1)
            self.assertEqual([metric_family], list(families))

    def test_escaping(self):
        families = text_string_to_metric_families("""# TYPE a counter
# HELP a he\\n\\\\l\\tp
{"a_total", foo="b\\"a\\nr"} 1
{"a_total", foo="b\\\\a\\z"} 2
{"a_total", foo="b\\"a\\nr # "} 3
{"a_total", foo="b\\\\a\\z # "} 4
# EOF
""")
        metric_family = CounterMetricFamily("a", "he\n\\l\\tp", labels=["foo"])
        metric_family.add_metric(["b\"a\nr"], 1)
        metric_family.add_metric(["b\\a\\z"], 2)
        metric_family.add_metric(["b\"a\nr # "], 3)
        metric_family.add_metric(["b\\a\\z # "], 4)
        self.assertEqual([metric_family], list(families))

    def test_null_byte(self):
        families = text_string_to_metric_families("""# TYPE a counter
# HELP a he\0lp
# EOF
""")
        metric_family = CounterMetricFamily("a", "he\0lp")
        self.assertEqual([metric_family], list(families))

    def test_timestamps(self):
        families = text_string_to_metric_families("""# TYPE a counter
# HELP a help
a_total{foo="1"} 1 000
a_total{foo="2"} 1 0.0
a_total{foo="3"} 1 1.1
a_total{foo="4"} 1 12345678901234567890.1234567890
a_total{foo="5"} 1 1.5e3
# TYPE b counter
# HELP b help
b_total 2 1234567890
# EOF
""")
        a = CounterMetricFamily("a", "help", labels=["foo"])
        a.add_metric(["1"], 1, timestamp=Timestamp(0, 0))
        a.add_metric(["2"], 1, timestamp=Timestamp(0, 0))
        a.add_metric(["3"], 1, timestamp=Timestamp(1, 100000000))
        a.add_metric(["4"], 1, timestamp=Timestamp(12345678901234567890, 123456789))
        a.add_metric(["5"], 1, timestamp=1500.0)
        b = CounterMetricFamily("b", "help")
        b.add_metric([], 2, timestamp=Timestamp(1234567890, 0))
        self.assertEqual([a, b], list(families))

    def test_hash_in_label_value(self):
        families = text_string_to_metric_families("""# TYPE a counter
# HELP a help
a_total{foo="foo # bar"} 1
a_total{foo="} foo # bar # "} 1
# EOF
""")
        a = CounterMetricFamily("a", "help", labels=["foo"])
        a.add_metric(["foo # bar"], 1)
        a.add_metric(["} foo # bar # "], 1)
        self.assertEqual([a], list(families))

    def test_exemplars_with_hash_in_label_values(self):
        families = text_string_to_metric_families("""# TYPE a histogram
# HELP a help
a_bucket{le="1.0",foo="bar # "} 0 # {a="b",foo="bar # bar"} 0.5
a_bucket{le="2.0",foo="bar # "} 2 # {a="c",foo="bar # bar"} 0.5
a_bucket{le="+Inf",foo="bar # "} 3 # {a="d",foo="bar # bar"} 4
# EOF
""")
        hfm = HistogramMetricFamily("a", "help")
        hfm.add_sample("a_bucket", {"le": "1.0", "foo": "bar # "}, 0.0, None, Exemplar({"a": "b", "foo": "bar # bar"}, 0.5))
        hfm.add_sample("a_bucket", {"le": "2.0", "foo": "bar # "}, 2.0, None, Exemplar({"a": "c", "foo": "bar # bar"}, 0.5))
        hfm.add_sample("a_bucket", {"le": "+Inf", "foo": "bar # "}, 3.0, None, Exemplar({"a": "d", "foo": "bar # bar"}, 4))
        self.assertEqual([hfm], list(families))


    def test_roundtrip(self):
        text = """# HELP go_gc_duration_seconds A summary of the GC invocation durations.
# TYPE go_gc_duration_seconds summary
go_gc_duration_seconds{quantile="0.0"} 0.013300656000000001
go_gc_duration_seconds{quantile="0.25"} 0.013638736
go_gc_duration_seconds{quantile="0.5"} 0.013759906
go_gc_duration_seconds{quantile="0.75"} 0.013962066
go_gc_duration_seconds{quantile="1.0"} 0.021383540000000003
go_gc_duration_seconds_sum 56.12904785
go_gc_duration_seconds_count 7476.0
# HELP go_goroutines Number of goroutines that currently exist.
# TYPE go_goroutines gauge
go_goroutines 166.0
# HELP prometheus_local_storage_indexing_batch_duration_milliseconds Quantiles for batch indexing duration in milliseconds.
# TYPE prometheus_local_storage_indexing_batch_duration_milliseconds summary
prometheus_local_storage_indexing_batch_duration_milliseconds{quantile="0.5"} NaN
prometheus_local_storage_indexing_batch_duration_milliseconds{quantile="0.9"} NaN
prometheus_local_storage_indexing_batch_duration_milliseconds{quantile="0.99"} NaN
prometheus_local_storage_indexing_batch_duration_milliseconds_sum 871.5665949999999
prometheus_local_storage_indexing_batch_duration_milliseconds_count 229.0
# HELP process_cpu_seconds Total user and system CPU time spent in seconds.
# TYPE process_cpu_seconds counter
process_cpu_seconds_total 29323.4
# HELP process_virtual_memory_bytes Virtual memory size in bytes.
# TYPE process_virtual_memory_bytes gauge
process_virtual_memory_bytes 2.478268416e+09
# HELP prometheus_build_info A metric with a constant '1' value labeled by version, revision, and branch from which Prometheus was built.
# TYPE prometheus_build_info gauge
prometheus_build_info{branch="HEAD",revision="ef176e5",version="0.16.0rc1"} 1.0
# HELP prometheus_local_storage_chunk_ops The total number of chunk operations by their type.
# TYPE prometheus_local_storage_chunk_ops counter
prometheus_local_storage_chunk_ops_total{type="clone"} 28.0
prometheus_local_storage_chunk_ops_total{type="create"} 997844.0
prometheus_local_storage_chunk_ops_total{type="drop"} 1.345758e+06
prometheus_local_storage_chunk_ops_total{type="load"} 1641.0
prometheus_local_storage_chunk_ops_total{type="persist"} 981408.0
prometheus_local_storage_chunk_ops_total{type="pin"} 32662.0
prometheus_local_storage_chunk_ops_total{type="transcode"} 980180.0
prometheus_local_storage_chunk_ops_total{type="unpin"} 32662.0
# HELP foo histogram Testing histogram buckets
# TYPE foo histogram
foo_bucket{le="0.0"} 0.0
foo_bucket{le="1e-05"} 0.0
foo_bucket{le="0.0001"} 0.0
foo_bucket{le="0.1"} 8.0
foo_bucket{le="1.0"} 10.0
foo_bucket{le="10.0"} 17.0
foo_bucket{le="100000.0"} 17.0
foo_bucket{le="1e+06"} 17.0
foo_bucket{le="1.55555555555552e+06"} 17.0
foo_bucket{le="1e+23"} 17.0
foo_bucket{le="+Inf"} 17.0
foo_count 17.0
foo_sum 324789.3
foo_created 1.520430000123e+09
# HELP bar histogram Testing with labels
# TYPE bar histogram
bar_bucket{a="b",le="+Inf"} 0.0
bar_bucket{a="c",le="+Inf"} 0.0
# EOF
"""
        families = list(text_string_to_metric_families(text))

        class TextCollector:
            def collect(self):
                return families

        registry = CollectorRegistry()
        registry.register(TextCollector())
        self.assertEqual(text.encode('utf-8'), generate_latest(registry))

    def test_invalid_input(self):
        for case in [
            # No EOF.
            (''),
            # Blank line
            ('a 1\n\n# EOF\n'),
            # Text after EOF.
            ('a 1\n# EOF\nblah'),
            ('a 1\n# EOFblah'),
            # Missing or wrong quotes on label value.
            ('a{a=1} 1\n# EOF\n'),
            ('a{a="1} 1\n# EOF\n'),
            ('a{a=\'1\'} 1\n# EOF\n'),
            ('"a" 1\n# EOF\n'),
            # Missing equal or label value.
            ('a{a} 1\n# EOF\n'),
            ('a{"a} 1\n# EOF\n'),
            ('a{a"value"} 1\n# EOF\n'),
            ('a{a""} 1\n# EOF\n'),
            ('a{a=} 1\n# EOF\n'),
            ('a{a="} 1\n# EOF\n'),
            # Missing delimiters.
            ('a{a="1"}1\n# EOF\n'),
            # Missing or extra commas.
            ('a{a="1"b="2"} 1\n# EOF\n'),
            ('a{a="1",,b="2"} 1\n# EOF\n'),
            ('a{a="1",b="2",} 1\n# EOF\n'),
            # Invalid labels.
            ('a{1="1"} 1\n# EOF\n'),
            ('a{1="1"}1\n# EOF\n'),
            ('a{a="1",a="1"} 1\n# EOF\n'),
            ('a{a="1"b} 1\n# EOF\n'),
            ('a{1=" # "} 1\n# EOF\n'),
            ('a{a=" # ",a=" # "} 1\n# EOF\n'),
            ('a{a=" # "}1\n# EOF\n'),
            ('a{a=" # ",b=}1\n# EOF\n'),
            ('a{a=" # "b}1\n# EOF\n'),
            # Missing value.
            ('a\n# EOF\n'),
            ('a \n# EOF\n'),
            # Bad HELP.
            ('# HELP\n# EOF\n'),
            ('# HELP \n# EOF\n'),
            ('# HELP a\n# EOF\n'),
            ('# HELP a\t\n# EOF\n'),
            (' # HELP a meh\n# EOF\n'),
            # Bad TYPE.
            ('# TYPE\n# EOF\n'),
            ('# TYPE \n# EOF\n'),
            ('# TYPE a\n# EOF\n'),
            ('# TYPE a\t\n# EOF\n'),
            ('# TYPE a meh\n# EOF\n'),
            ('# TYPE a meh \n# EOF\n'),
            ('# TYPE a gauge \n# EOF\n'),
            ('# TYPE a untyped\n# EOF\n'),
            # Bad UNIT.
            ('# UNIT\n# EOF\n'),
            ('# UNIT \n# EOF\n'),
            ('# UNIT a\n# EOF\n'),
            ('# UNIT a\t\n# EOF\n'),
            ('# UNIT a seconds\n# EOF\n'),
            ('# UNIT a_seconds seconds \n# EOF\n'),
            ('# TYPE x_u info\n# UNIT x_u u\n# EOF\n'),
            ('# TYPE x_u stateset\n# UNIT x_u u\n# EOF\n'),
            # Metadata in wrong place.
            ('# HELP a x\na 1\n# TYPE a gauge\n# EOF\n'),
            ('# TYPE a gauge\na 1\n# HELP a gauge\n# EOF\n'),
            ('# TYPE a_s gauge\na_s 1\n# UNIT a_s s\n# EOF\n'),
            # Repeated metadata.
            ('# HELP a \n# HELP a \n# EOF\n'),
            ('# HELP a x\n# HELP a x\n# EOF\n'),
            ('# TYPE a untyped\n# TYPE a untyped\n# EOF\n'),
            ('# UNIT a_s s\n# UNIT a_s s\n# EOF\n'),
            # Bad metadata.
            ('# FOO a x\n# EOF\n'),
            # Bad metric names.
            ('0a 1\n# EOF\n'),
            ('a.b 1\n# EOF\n'),
            ('a-b 1\n# EOF\n'),
            # Bad value.
            ('a a\n# EOF\n'),
            ('a  1\n# EOF\n'),
            ('a 1\t\n# EOF\n'),
            ('a 1 \n# EOF\n'),
            ('a 1_2\n# EOF\n'),
            ('a 0x1p-3\n# EOF\n'),
            ('a 0x1P-3\n# EOF\n'),
            ('a 0b1\n# EOF\n'),
            ('a 0B1\n# EOF\n'),
            ('a 0x1\n# EOF\n'),
            ('a 0X1\n# EOF\n'),
            ('a 0o1\n# EOF\n'),
            ('a 0O1\n# EOF\n'),
            # Bad timestamp.
            ('a 1 z\n# EOF\n'),
            ('a 1 1z\n# EOF\n'),
            ('a 1 1_2\n# EOF\n'),
            ('a 1 1.1.1\n# EOF\n'),
            ('a 1 NaN\n# EOF\n'),
            ('a 1 Inf\n# EOF\n'),
            ('a 1 +Inf\n# EOF\n'),
            ('a 1 -Inf\n# EOF\n'),
            ('a 1 0x1p-3\n# EOF\n'),
            # Bad exemplars.
            ('# TYPE a histogram\na_bucket{le="+Inf"} 1 #\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 1# {} 1\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 1 #{} 1\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 1 # {}1\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 1 # {} 1 \n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 1 # {} 1 1 \n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 1 # '
             '{a="23456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"} 1 1\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 1 # {} 0x1p-3\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 1 # {} 1 0x1p-3\n# EOF\n'),
            ('# TYPE a counter\na_total 1 1 # {id="a"}  \n# EOF\n'),
            ('# TYPE a counter\na_total 1 1 # id="a"} 1\n# EOF\n'),
            ('# TYPE a counter\na_total 1 1 #id=" # "} 1\n# EOF\n'),
            ('# TYPE a counter\na_total 1 1 id=" # "} 1\n# EOF\n'),
            # Exemplars on unallowed samples.
            ('# TYPE a histogram\na_sum 1 # {a="b"} 0.5\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_sum 1 # {a="b"} 0.5\n# EOF\n'),
            ('# TYPE a_bucket gauge\na_bucket 1 # {a="b"} 0.5\n# EOF\n'),
            ('# TYPE a counter\na_created 1 # {a="b"} 0.5\n# EOF\n'),
            # Exemplars on unallowed metric types.
            ('# TYPE a gauge\na 1 # {a="b"} 1\n# EOF\n'),
            ('# TYPE a info\na_info 1 # {a="b"} 1\n# EOF\n'),
            ('# TYPE a stateset\na{a="b"} 1 # {c="d"} 1\n# EOF\n'),
            # Bad stateset/info values.
            ('# TYPE a stateset\na 2\n# EOF\n'),
            ('# TYPE a info\na 2\n# EOF\n'),
            ('# TYPE a stateset\na 2.0\n# EOF\n'),
            ('# TYPE a info\na 2.0\n# EOF\n'),
            # Missing or invalid labels for a type.
            ('# TYPE a summary\na 0\n# EOF\n'),
            ('# TYPE a summary\na{quantile="-1"} 0\n# EOF\n'),
            ('# TYPE a summary\na{quantile="foo"} 0\n# EOF\n'),
            ('# TYPE a summary\na{quantile="1.01"} 0\n# EOF\n'),
            ('# TYPE a summary\na{quantile="NaN"} 0\n# EOF\n'),
            ('# TYPE a histogram\na_bucket 0\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_bucket 0\n# EOF\n'),
            ('# TYPE a stateset\na 0\n# EOF\n'),
            # Bad counter values.
            ('# TYPE a counter\na_total NaN\n# EOF\n'),
            ('# TYPE a counter\na_total -1\n# EOF\n'),
            ('# TYPE a histogram\na_sum NaN\n# EOF\n'),
            ('# TYPE a histogram\na_count NaN\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} NaN\n# EOF\n'),
            ('# TYPE a histogram\na_sum -1\n# EOF\n'),
            ('# TYPE a histogram\na_count -1\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} -1\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="-1.0"} 1\na_bucket{le="+Inf"} 2\na_sum -1\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="-1.0"} 1\na_bucket{le="+Inf"} 2\na_sum 1\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 0.5\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 0.5\na_count 0.5\na_sum 0\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} NaN\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} -1\na_gcount -1\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} -1\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} 1\na_gsum -1\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} 1\na_gsum NaN\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} 0.5\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} 0.5\na_gsum 0.5\na_gcount 0\n# EOF\n'),
            ('# TYPE a summary\na_sum NaN\n# EOF\n'),
            ('# TYPE a summary\na_count NaN\n# EOF\n'),
            ('# TYPE a summary\na_sum -1\n# EOF\n'),
            ('# TYPE a summary\na_count -1\n# EOF\n'),
            ('# TYPE a summary\na_count 0.5\n# EOF\n'),
            ('# TYPE a summary\na{quantile="0.5"} -1\n# EOF\n'),
            # Bad info and stateset values.
            ('# TYPE a info\na_info{foo="bar"} 2\n# EOF\n'),
            ('# TYPE a stateset\na{a="bar"} 2\n# EOF\n'),
            # Bad histograms.
            ('# TYPE a histogram\na_sum 1\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 0\na_sum 0\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 0\na_count 0\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="-1"} 0\na_bucket{le="+Inf"} 0\na_sum 0\na_count 0\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_gsum 1\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} 0\na_gsum 0\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} 0\na_gcount 0\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_bucket{le="+Inf"} 1\na_gsum -1\na_gcount 1\n# EOF\n'),
            ('# TYPE a histogram\na_count 1\na_bucket{le="+Inf"} 0\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+Inf"} 0\na_count 1\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="+INF"} 0\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="2"} 0\na_bucket{le="1"} 0\na_bucket{le="+Inf"} 0\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{le="1"} 1\na_bucket{le="2"} 1\na_bucket{le="+Inf"} 0\n# EOF\n'),
            ('# TYPE a histogram\na_bucket {} {}'),
            # Bad grouping or ordering.
            ('# TYPE a histogram\na_sum{a="1"} 0\na_sum{a="2"} 0\na_count{a="1"} 0\n# EOF\n'),
            ('# TYPE a histogram\na_bucket{a="1",le="1"} 0\na_bucket{a="2",le="+Inf""} '
             '0\na_bucket{a="1",le="+Inf"} 0\n# EOF\n'),
            ('# TYPE a gaugehistogram\na_gsum{a="1"} 0\na_gsum{a="2"} 0\na_gcount{a="1"} 0\n# EOF\n'),
            ('# TYPE a summary\nquantile{quantile="0"} 0\na_sum{a="1"} 0\nquantile{quantile="1"} 0\n# EOF\n'),
            ('# TYPE a gauge\na 0 -1\na 0 -2\n# EOF\n'),
            ('# TYPE a gauge\na 0 -1\na 0 -1.1\n# EOF\n'),
            ('# TYPE a gauge\na 0 1\na 0 -1\n# EOF\n'),
            ('# TYPE a gauge\na 0 1.1\na 0 1\n# EOF\n'),
            ('# TYPE a gauge\na 0 1\na 0 0\n# EOF\n'),
            ('# TYPE a gauge\na 0\na 0 0\n# EOF\n'),
            ('# TYPE a gauge\na 0 0\na 0\n# EOF\n'),
            # Clashing names.
            ('# TYPE a counter\n# TYPE a counter\n# EOF\n'),
            ('# TYPE a info\n# TYPE a counter\n# EOF\n'),
            ('# TYPE a_created gauge\n# TYPE a counter\n# EOF\n'),
            # Bad native histograms.
            ('# TYPE nh histogram\nnh {count:24\n# EOF\n'),
            ('# TYPE nh histogram\nnh{} # {count:24\n# EOF\n'),

        ]:
            with self.assertRaises(ValueError, msg=case):
                list(text_string_to_metric_families(case))


if __name__ == '__main__':
    unittest.main()
