From ebb58300750026ed93f5501e392f5d31f88f4b03 Mon Sep 17 00:00:00 2001 From: Doychin Bondzhev Date: Wed, 2 Jan 2019 14:51:13 +0200 Subject: [PATCH] Some of the returned values are performance dependant - stddev for histogram This one is more complex as change. In order to reuse existing expected values and filter the value that is not always the same I've implemented some parsing before comparing the returned string with expected string. In case you don't like it I'm open for suggestions. --- .../histogram/WeatherServiceTest.java | 178 +++++++++++------- 1 file changed, 110 insertions(+), 68 deletions(-) diff --git a/examples/mp-metrics-histogram/src/test/java/org/superbiz/histogram/WeatherServiceTest.java b/examples/mp-metrics-histogram/src/test/java/org/superbiz/histogram/WeatherServiceTest.java index 1f27616f998..1253c7ee62d 100644 --- a/examples/mp-metrics-histogram/src/test/java/org/superbiz/histogram/WeatherServiceTest.java +++ b/examples/mp-metrics-histogram/src/test/java/org/superbiz/histogram/WeatherServiceTest.java @@ -31,6 +31,7 @@ import javax.json.Json; import javax.json.JsonObject; +import javax.json.stream.JsonParser; import javax.ws.rs.client.Client; import javax.ws.rs.client.ClientBuilder; import javax.ws.rs.client.WebTarget; @@ -40,6 +41,8 @@ import java.io.StringReader; import java.net.URL; +import java.util.*; +import java.util.stream.Collectors; import java.util.stream.Stream; import static junit.framework.TestCase.assertTrue; @@ -51,8 +54,9 @@ public class WeatherServiceTest { @Deployment(testable = false) public static WebArchive createDeployment() { final WebArchive webArchive = ShrinkWrap.create(WebArchive.class, "test.war") - .addClass(WeatherService.class) - .addAsWebInfResource(new StringAsset(""), "beans.xml"); + .addClass(WeatherService.class) + .addAsWebInfResource(new StringAsset(""), + "beans.xml"); return webArchive; } @@ -74,9 +78,7 @@ public void after() { @Test public void testHistogramMetric() { WebTarget webTarget = this.client.target(this.base.toExternalForm()); - final String message = webTarget.path("/weather/histogram") - .request() - .get(String.class); + final String message = webTarget.path("/weather/histogram").request().get(String.class); final String metricPath = "/metrics/application"; assertPrometheusFormat(metricPath); assertJsonFormat(metricPath); @@ -84,81 +86,121 @@ public void testHistogramMetric() { private void assertPrometheusFormat(final String metricPath) { WebTarget webTarget = this.client.target(this.base.toExternalForm()); - final String metric = webTarget.path(metricPath) - .request() - .accept(MediaType.TEXT_PLAIN) - .get(String.class); - final String expected = "# TYPE application:temperatures_degrees F summary histogram\n" + - "# TYPE application:temperatures_degrees F_count histogram\n" + - "application:temperatures_degrees F_count 15.0\n" + - "# TYPE application:temperatures_min_degrees F histogram\n" + - "application:temperatures_min_degrees F 27.0\n" + - "# TYPE application:temperatures_max_degrees F histogram\n" + - "application:temperatures_max_degrees F 55.0\n" + - "# TYPE application:temperatures_mean_degrees F histogram\n" + - "application:temperatures_mean_degrees F 44.4\n" + - "# TYPE application:temperatures_stddev_degrees F histogram\n" + - "application:temperatures_stddev_degrees F 7.0710678118654755\n" + - "# TYPE application:temperatures_degrees F histogram\n" + - "application:temperatures_degrees F{quantile=\"0.5\"} 45.0\n" + - "# TYPE application:temperatures_degrees F histogram\n" + - "application:temperatures_degrees F{quantile=\"0.75\"} 46.0\n" + - "# TYPE application:temperatures_degrees F histogram\n" + - "application:temperatures_degrees F{quantile=\"0.95\"} 54.0\n" + - "# TYPE application:temperatures_degrees F histogram\n" + - "application:temperatures_degrees F{quantile=\"0.98\"} 54.0\n" + - "# TYPE application:temperatures_degrees F histogram\n" + - "application:temperatures_degrees F{quantile=\"0.99\"} 54.0\n" + - "# TYPE application:temperatures_degrees F histogram\n" + - "application:temperatures_degrees F{quantile=\"0.999\"} 54.0\n" + - "# TYPE application:org_superbiz_histogram_weather_service_temperatures summary histogram\n" + - "# TYPE application:org_superbiz_histogram_weather_service_temperatures_count histogram\n" + - "application:org_superbiz_histogram_weather_service_temperatures_count 0.0\n" + - "# TYPE application:org_superbiz_histogram_weather_service_temperatures_min histogram\n" + - "application:org_superbiz_histogram_weather_service_temperatures_min 0.0\n" + - "# TYPE application:org_superbiz_histogram_weather_service_temperatures_max histogram\n" + - "application:org_superbiz_histogram_weather_service_temperatures_max 0.0\n" + - "# TYPE application:org_superbiz_histogram_weather_service_temperatures_mean histogram\n" + - "application:org_superbiz_histogram_weather_service_temperatures_mean NaN\n" + - "# TYPE application:org_superbiz_histogram_weather_service_temperatures_stddev histogram\n" + - "application:org_superbiz_histogram_weather_service_temperatures_stddev 0.0\n" + - "# TYPE application:org_superbiz_histogram_weather_service_temperatures histogram\n" + - "application:org_superbiz_histogram_weather_service_temperatures{quantile=\"0.5\"} 0.0\n" + - "# TYPE application:org_superbiz_histogram_weather_service_temperatures histogram\n" + - "application:org_superbiz_histogram_weather_service_temperatures{quantile=\"0.75\"} 0.0\n" + - "# TYPE application:org_superbiz_histogram_weather_service_temperatures histogram\n" + - "application:org_superbiz_histogram_weather_service_temperatures{quantile=\"0.95\"} 0.0\n" + - "# TYPE application:org_superbiz_histogram_weather_service_temperatures histogram\n" + - "application:org_superbiz_histogram_weather_service_temperatures{quantile=\"0.98\"} 0.0\n" + - "# TYPE application:org_superbiz_histogram_weather_service_temperatures histogram\n" + - "application:org_superbiz_histogram_weather_service_temperatures{quantile=\"0.99\"} 0.0\n" + - "# TYPE application:org_superbiz_histogram_weather_service_temperatures histogram\n" + - "application:org_superbiz_histogram_weather_service_temperatures{quantile=\"0.999\"} 0.0\n"; - assertEquals(expected, metric); + final String[] metric = + webTarget.path(metricPath).request().accept(MediaType.TEXT_PLAIN).get(String.class).split("\n"); + final Set expected = new HashSet<>(Arrays.asList( + ("# TYPE application:temperatures_degrees F summary histogram\n" + "# TYPE " + "application" + + ":temperatures_degrees F_count histogram\n" + "application:temperatures_degrees " + + "F_count 15.0\n" + "# TYPE application:temperatures_min_degrees F histogram\n" + + "application:temperatures_min_degrees F 27.0\n" + "# TYPE " + "application" + + ":temperatures_max_degrees F histogram\n" + "application" + ":temperatures_max_degrees F " + + "55" + ".0\n" + "# TYPE application:temperatures_mean_degrees F " + "histogram\n" + + "application" + ":temperatures_mean_degrees F 44.4\n" + "# TYPE " + "application" + + ":temperatures_stddev_degrees F histogram\n" + "application" + + ":temperatures_stddev_degrees F 7.0710678118654755\n" + "# TYPE " + "application" + + ":temperatures_degrees F histogram\n" + "application:temperatures_degrees " + "F{quantile" + "=\"0.5\"} 45.0\n" + "# TYPE application:temperatures_degrees F histogram\n" + "application" + ":temperatures_degrees F{quantile=\"0.75\"} 46.0\n" + "# TYPE " + "application" + ":temperatures_degrees F histogram\n" + "application:temperatures_degrees " + "F{quantile" + "=\"0.95\"} 54.0\n" + "# TYPE application:temperatures_degrees F histogram\n" + "application:temperatures_degrees F{quantile=\"0.98\"} 54.0\n" + "# TYPE " + "application" + ":temperatures_degrees F histogram\n" + "application:temperatures_degrees " + "F{quantile" + "=\"0.99\"} 54.0\n" + "# TYPE application:temperatures_degrees F histogram\n" + "application:temperatures_degrees F{quantile=\"0.999\"} 54.0\n" + "# TYPE " + "application" + ":org_superbiz_histogram_weather_service_temperatures summary histogram\n" + "# " + "TYPE " + "application:org_superbiz_histogram_weather_service_temperatures_count histogram\n" + "application:org_superbiz_histogram_weather_service_temperatures_count 0.0\n" + "# TYPE " + "application:org_superbiz_histogram_weather_service_temperatures_min histogram\n" + "application:org_superbiz_histogram_weather_service_temperatures_min 0.0\n" + "# TYPE " + "application:org_superbiz_histogram_weather_service_temperatures_max histogram\n" + "application:org_superbiz_histogram_weather_service_temperatures_max 0.0\n" + "# TYPE " + "application:org_superbiz_histogram_weather_service_temperatures_mean histogram\n" + "application:org_superbiz_histogram_weather_service_temperatures_mean 0.0\n" + "# TYPE " + "application:org_superbiz_histogram_weather_service_temperatures_stddev histogram\n" + "application:org_superbiz_histogram_weather_service_temperatures_stddev 0.0\n" + "# TYPE " + "application:org_superbiz_histogram_weather_service_temperatures histogram\n" + "application:org_superbiz_histogram_weather_service_temperatures{quantile=\"0.5\"} 0.0\n" + "# TYPE application:org_superbiz_histogram_weather_service_temperatures histogram\n" + "application:org_superbiz_histogram_weather_service_temperatures{quantile=\"0.75\"} 0.0\n" + "# TYPE application:org_superbiz_histogram_weather_service_temperatures histogram\n" + "application:org_superbiz_histogram_weather_service_temperatures{quantile=\"0.95\"} 0.0\n" + "# TYPE application:org_superbiz_histogram_weather_service_temperatures histogram\n" + "application:org_superbiz_histogram_weather_service_temperatures{quantile=\"0.98\"} 0.0\n" + "# TYPE application:org_superbiz_histogram_weather_service_temperatures histogram\n" + "application:org_superbiz_histogram_weather_service_temperatures{quantile=\"0.99\"} 0.0\n" + "# TYPE application:org_superbiz_histogram_weather_service_temperatures histogram\n" + "application:org_superbiz_histogram_weather_service_temperatures{quantile=\"0.999\"} 0.0\n") + .split("\n"))); + + final Set result = + Stream.of(metric).filter(m -> !expected.contains(m)).collect(Collectors.toSet()); + // There should be only one line left in result. That line contains the value for + // temperatures_stddev_degrees + assertEquals(1, result.size()); + // The part after 7. is performance dependant and is never the same for every new call + assertTrue(result.iterator().next().startsWith("application:temperatures_stddev_degrees F 7.")); + } + + class JsonItem { + + JsonParser.Event event; + + String value; + + JsonItem(JsonParser.Event event) { + this.event = event; + } + + JsonItem(JsonParser.Event event, String value) { + this.event = event; + this.value = value; + } + + @Override + public boolean equals(Object o) { + if (this == o) + return true; + if (o == null || getClass() != o.getClass()) + return false; + + JsonItem jsonItem = (JsonItem) o; + + if (event != jsonItem.event) + return false; + return Objects.equals(value, jsonItem.value); + } + + @Override + public int hashCode() { + int result = event != null ? event.hashCode() : 0; + result = 31 * result + (value != null ? value.hashCode() : 0); + return result; + } } private void assertJsonFormat(final String metricPath) { WebTarget webTarget = this.client.target(this.base.toExternalForm()); - final String metric = webTarget.path(metricPath) - .request() - .accept(MediaType.APPLICATION_JSON) - .get(String.class); - final String expected = "{\"temperatures\":{\"count\":15,\"max\":55,\"mean\":44.4,\"min\":27,\"p50\":45.0,\"p75\":46.0,\"p95\":54.0,\"p98\":54.0,\"p99\":54.0,\"p999\":54.0,\"stddev\":7.0710678118654755,\"unit\":\"degrees F\"},\"org.superbiz.histogram.WeatherService.temperatures\":{\"count\":0,\"max\":0,\"min\":0,\"p50\":0.0,\"p75\":0.0,\"p95\":0.0,\"p98\":0.0,\"p99\":0.0,\"p999\":0.0,\"stddev\":0.0,\"unit\":\"none\"}}"; - assertEquals(expected, metric); + final String metric = + webTarget.path(metricPath).request().accept(MediaType.APPLICATION_JSON).get(String.class); + + List expectedList = convertToMap( + "{\"temperatures\":{\"count\":15,\"max\":55,\"mean\":44.4,\"min\":27,\"p50\":45.0,\"p75\":46.0,\"p95\":54.0,\"p98\":54.0,\"p99\":54.0,\"p999\":54.0,\"stddev\":7.0710678118654755,\"unit\":\"degrees F\"},\"org.superbiz.histogram.WeatherService.temperatures\":{\"count\":0,\"max\":0,\"mean\":0.0,\"min\":0,\"p50\":0.0,\"p75\":0.0,\"p95\":0.0,\"p98\":0.0,\"p99\":0.0,\"p999\":0.0,\"stddev\":0.0,\"unit\":\"none\"}}"); + + List metricList = convertToMap(metric); + assertEquals(expectedList.size(), metricList.size()); + + removeStdDevValue(metricList); + removeStdDevValue(expectedList); + + assertEquals(expectedList, metricList); + } + + private void removeStdDevValue(List list) { + // Check for value 7.xxxxxxxxxxxxx. + Optional stddevValue = list.stream().filter(item->item.event == JsonParser.Event.VALUE_NUMBER && item.value.startsWith("7.") && item.value.length() > 3).findFirst(); + assertTrue(stddevValue.isPresent()); + + list.remove(stddevValue.get()); + } + + private List convertToMap(String s) { + JsonParser expectedParser = Json.createParser(new StringReader(s)); + List list = new ArrayList<>(); + while (expectedParser.hasNext()) { + JsonParser.Event event = expectedParser.next(); + switch (event) { + case KEY_NAME: + case VALUE_STRING: + case VALUE_NUMBER: + list.add(new JsonItem(event, expectedParser.getValue().toString())); + break; + default: + list.add(new JsonItem(event)); + } + } + return list; } @Test public void testHistogramMetricMetadata() { WebTarget webTarget = this.client.target(this.base.toExternalForm()); - final Response response = webTarget - .path("/metrics/application") - .request() - .accept(MediaType.APPLICATION_JSON) - .options(); + final Response response = + webTarget.path("/metrics/application").request().accept(MediaType.APPLICATION_JSON).options(); final String metaData = response.readEntity(String.class); JsonObject metadataJson = Json.createReader(new StringReader(metaData)).readObject(); - final String expected = "{\"temperatures\":{\"description\":\"A histogram of recent New York temperatures.\",\"displayName\":\"temperatures\",\"name\":\"temperatures\",\"reusable\":false,\"tags\":\"\",\"type\":\"histogram\",\"typeRaw\":\"HISTOGRAM\",\"unit\":\"degrees F\"},\"org.superbiz.histogram.WeatherService.temperatures\":{\"description\":\"A histogram metrics example.\",\"displayName\":\"Histogram of Recent New York Temperatures\",\"name\":\"org.superbiz.histogram.WeatherService.temperatures\",\"reusable\":false,\"tags\":\"\",\"type\":\"histogram\",\"typeRaw\":\"HISTOGRAM\",\"unit\":\"none\"}}"; + final String expected = + "{\"temperatures\":{\"description\":\"A histogram of recent New York temperatures.\"," + + "\"displayName\":\"temperatures\",\"name\":\"temperatures\",\"reusable\":false," + "\"tags" + "\":\"\",\"type\":\"histogram\",\"typeRaw\":\"HISTOGRAM\",\"unit\":\"degrees F\"}," + "\"org.superbiz.histogram.WeatherService.temperatures\":{\"description\":\"A histogram " + "metrics example.\",\"displayName\":\"Histogram of Recent New York Temperatures\"," + "\"name\":\"org.superbiz.histogram.WeatherService.temperatures\",\"reusable\":false," + "\"tags\":\"\",\"type\":\"histogram\",\"typeRaw\":\"HISTOGRAM\",\"unit\":\"none\"}}"; JsonObject expectedJson = Json.createReader(new StringReader(expected)).readObject(); assertEquals(expectedJson, metadataJson);