From 0910ebe21cee884b0cee2fd086355fa5173252ed Mon Sep 17 00:00:00 2001 From: Tanguy Leroux Date: Tue, 18 Apr 2017 17:03:32 +0200 Subject: [PATCH] [Test] Use appropriate DocValueFormats in Aggregations tests (#24155) Some aggregations (like Min, Max etc) use a wrong DocValueFormat in tests (like IP or GeoHash). We should not test aggregations that expect a numeric value with a DocValueFormat like IP. Such wrong DocValueFormat can also prevent the aggregation to be rendered as ToXContent, and this will be an issue for the High Level Rest Client tests which expect to be able to parse back aggregations. --- .../elasticsearch/search/DocValueFormat.java | 17 ++++++ .../search/DocValueFormatTests.java | 30 +++++++++- .../InternalAggregationTestCase.java | 13 +++++ ...ternalSingleBucketAggregationTestCase.java | 57 +++++++++++++++++++ .../metrics/InternalMaxTests.java | 2 +- .../metrics/avg/InternalAvgTests.java | 2 +- .../metrics/min/InternalMinTests.java | 3 +- .../pipeline/InternalSimpleValueTests.java | 2 - .../derivative/InternalDerivativeTests.java | 2 - 9 files changed, 118 insertions(+), 10 deletions(-) create mode 100644 core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java diff --git a/core/src/main/java/org/elasticsearch/search/DocValueFormat.java b/core/src/main/java/org/elasticsearch/search/DocValueFormat.java index 4c32667aa2acc..eb76db3be687b 100644 --- a/core/src/main/java/org/elasticsearch/search/DocValueFormat.java +++ b/core/src/main/java/org/elasticsearch/search/DocValueFormat.java @@ -393,5 +393,22 @@ public double parseDouble(String value, boolean roundUp, LongSupplier now) { public BytesRef parseBytesRef(String value) { throw new UnsupportedOperationException(); } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Decimal that = (Decimal) o; + return Objects.equals(pattern, that.pattern); + } + + @Override + public int hashCode() { + return Objects.hash(pattern); + } } } diff --git a/core/src/test/java/org/elasticsearch/search/DocValueFormatTests.java b/core/src/test/java/org/elasticsearch/search/DocValueFormatTests.java index 049e133b35740..7bf5308eb635e 100644 --- a/core/src/test/java/org/elasticsearch/search/DocValueFormatTests.java +++ b/core/src/test/java/org/elasticsearch/search/DocValueFormatTests.java @@ -19,9 +19,6 @@ package org.elasticsearch.search; -import java.util.ArrayList; -import java.util.List; - import org.apache.lucene.document.InetAddressPoint; import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.io.stream.BytesStreamOutput; @@ -34,6 +31,9 @@ import org.elasticsearch.test.ESTestCase; import org.joda.time.DateTimeZone; +import java.util.ArrayList; +import java.util.List; + public class DocValueFormatTests extends ESTestCase { public void testSerialization() throws Exception { @@ -108,6 +108,18 @@ public void testIpFormat() { DocValueFormat.IP.format(new BytesRef(InetAddressPoint.encode(InetAddresses.forString("::1"))))); } + public void testDecimalFormat() { + DocValueFormat formatter = new DocValueFormat.Decimal("###.##"); + assertEquals("0", formatter.format(0.0d)); + assertEquals("1", formatter.format(1d)); + formatter = new DocValueFormat.Decimal("000.000"); + assertEquals("-000.500", formatter.format(-0.5)); + formatter = new DocValueFormat.Decimal("###,###.###"); + assertEquals("0.86", formatter.format(0.8598023539251286d)); + formatter = new DocValueFormat.Decimal("###,###.###"); + assertEquals("859,802.354", formatter.format(0.8598023539251286d * 1_000_000)); + } + public void testRawParse() { assertEquals(-1L, DocValueFormat.RAW.parseLong("-1", randomBoolean(), null)); assertEquals(1L, DocValueFormat.RAW.parseLong("1", randomBoolean(), null)); @@ -145,4 +157,16 @@ public void testIPParse() { assertEquals(new BytesRef(InetAddressPoint.encode(InetAddresses.forString("::1"))), DocValueFormat.IP.parseBytesRef("::1")); } + + public void testDecimalParse() { + DocValueFormat parser = new DocValueFormat.Decimal("###.##"); + assertEquals(0.0d, parser.parseDouble(randomFrom("0.0", "0", ".0", ".0000"), true, null), 0.0d); + assertEquals(-1.0d, parser.parseDouble(randomFrom("-1.0", "-1", "-1.0", "-1.0000"), true, null), 0.0d); + assertEquals(0.0d, parser.parseLong("0", true, null), 0.0d); + assertEquals(1.0d, parser.parseLong("1", true, null), 0.0d); + parser = new DocValueFormat.Decimal("###,###.###"); + assertEquals(859802.354d, parser.parseDouble("859,802.354", true, null), 0.0d); + assertEquals(0.859d, parser.parseDouble("0.859", true, null), 0.0d); + assertEquals(0.8598023539251286d, parser.parseDouble("0.8598023539251286", true, null), 0.0d); + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java index a8f5363bcf93d..ac58866cb3e2b 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/InternalAggregationTestCase.java @@ -45,6 +45,7 @@ import org.elasticsearch.search.aggregations.metrics.valuecount.ValueCountAggregationBuilder; import org.elasticsearch.search.aggregations.pipeline.InternalSimpleValue; import org.elasticsearch.search.aggregations.pipeline.ParsedSimpleValue; +import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import org.elasticsearch.test.ESTestCase; import org.elasticsearch.search.aggregations.pipeline.derivative.DerivativePipelineAggregationBuilder; @@ -56,6 +57,7 @@ import java.util.List; import java.util.Map; import java.util.stream.Collectors; +import java.util.function.Supplier; import static java.util.Collections.*; import static org.elasticsearch.common.xcontent.XContentHelper.toXContent; @@ -149,4 +151,15 @@ public final void testFromXContent() throws IOException { //norelease TODO make abstract protected void assertFromXContent(T aggregation, ParsedAggregation parsedAggregation) { } + + /** + * @return a random {@link DocValueFormat} that can be used in aggregations which + * compute numbers. + */ + protected static DocValueFormat randomNumericDocValueFormat() { + final List> formats = new ArrayList<>(3); + formats.add(() -> DocValueFormat.RAW); + formats.add(() -> new DocValueFormat.Decimal(randomFrom("###.##", "###,###.##"))); + return randomFrom(formats).get(); + } } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java new file mode 100644 index 0000000000000..0e53d56377eb6 --- /dev/null +++ b/core/src/test/java/org/elasticsearch/search/aggregations/bucket/InternalSingleBucketAggregationTestCase.java @@ -0,0 +1,57 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.elasticsearch.search.aggregations.bucket; + +import org.elasticsearch.search.aggregations.InternalAggregation; +import org.elasticsearch.search.aggregations.InternalAggregationTestCase; +import org.elasticsearch.search.aggregations.InternalAggregations; +import org.elasticsearch.search.aggregations.metrics.max.InternalMax; +import org.elasticsearch.search.aggregations.metrics.min.InternalMin; +import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; + +public abstract class InternalSingleBucketAggregationTestCase + extends InternalAggregationTestCase { + private final boolean hasInternalMax = randomBoolean(); + private final boolean hasInternalMin = randomBoolean(); + + protected abstract T createTestInstance(String name, long docCount, InternalAggregations aggregations, + List pipelineAggregators, Map metaData); + + @Override + protected final T createTestInstance(String name, List pipelineAggregators, Map metaData) { + List internal = new ArrayList<>(); + if (hasInternalMax) { + internal.add(new InternalMax("max", randomDouble(), randomNumericDocValueFormat(), emptyList(), emptyMap())); + } + if (hasInternalMin) { + internal.add(new InternalMin("min", randomDouble(), randomNumericDocValueFormat(), emptyList(), emptyMap())); + } + // we shouldn't use the full long range here since we sum doc count on reduce, and don't want to overflow the long range there + long docCount = between(0, Integer.MAX_VALUE); + return createTestInstance(name, docCount, new InternalAggregations(internal), pipelineAggregators, metaData); + } +} diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java index c084e2173c032..3f9c52d6cd554 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/InternalMaxTests.java @@ -19,12 +19,12 @@ package org.elasticsearch.search.aggregations.metrics; + import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.metrics.max.InternalMax; import org.elasticsearch.search.aggregations.metrics.max.ParsedMax; -import org.elasticsearch.search.aggregations.metrics.min.InternalMin; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; import java.util.List; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java index eac447c3c48ab..10a952e330483 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/avg/InternalAvgTests.java @@ -32,7 +32,7 @@ public class InternalAvgTests extends InternalAggregationTestCase { @Override protected InternalAvg createTestInstance(String name, List pipelineAggregators, Map metaData) { - DocValueFormat formatter = randomFrom(new DocValueFormat.Decimal("###.##"), DocValueFormat.BOOLEAN, DocValueFormat.RAW); + DocValueFormat formatter = randomNumericDocValueFormat(); long count = frequently() ? randomNonNegativeLong() % 100000 : 0; return new InternalAvg(name, randomDoubleBetween(0, 100000, true), count, formatter, pipelineAggregators, metaData); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java index 44105f82b97a8..4315a5bbee80c 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/metrics/min/InternalMinTests.java @@ -20,6 +20,7 @@ package org.elasticsearch.search.aggregations.metrics.min; import org.elasticsearch.search.DocValueFormat; +import org.elasticsearch.search.aggregations.Aggregations; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; @@ -31,7 +32,7 @@ public class InternalMinTests extends InternalAggregationTestCase { @Override protected InternalMin createTestInstance(String name, List pipelineAggregators, Map metaData) { double value = frequently() ? randomDouble() : randomFrom(new Double[] { Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY }); - DocValueFormat formatter = randomFrom(new DocValueFormat.Decimal("###.##"), DocValueFormat.BOOLEAN, DocValueFormat.RAW); + DocValueFormat formatter = randomNumericDocValueFormat(); return new InternalMin(name, value, formatter, pipelineAggregators, metaData); } diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/InternalSimpleValueTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/InternalSimpleValueTests.java index 24ec171014cd6..dcde6311cdffd 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/InternalSimpleValueTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/InternalSimpleValueTests.java @@ -19,12 +19,10 @@ package org.elasticsearch.search.aggregations.pipeline; -import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.ParsedAggregation; -import java.util.Collections; import java.util.List; import java.util.Map; diff --git a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/derivative/InternalDerivativeTests.java b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/derivative/InternalDerivativeTests.java index cae86562cd718..7c0fa0aa4a9d0 100644 --- a/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/derivative/InternalDerivativeTests.java +++ b/core/src/test/java/org/elasticsearch/search/aggregations/pipeline/derivative/InternalDerivativeTests.java @@ -19,13 +19,11 @@ package org.elasticsearch.search.aggregations.pipeline.derivative; -import org.elasticsearch.common.io.stream.Writeable.Reader; import org.elasticsearch.search.DocValueFormat; import org.elasticsearch.search.aggregations.InternalAggregationTestCase; import org.elasticsearch.search.aggregations.ParsedAggregation; import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator; -import java.util.Collections; import java.util.List; import java.util.Map;