Skip to content

Commit feed66f

Browse files
committed
[TEST] Increase InternalDateHistogramTests coverage (#36064)
In this test we were randomizing different values but minDocCount was hardcoded to 1. It's important to test other values, especially `0` as it's the default. The test needed some adapting in the way buckets are randomly generated: all aggs need to share the same interval, minDocCount and emptyBucketInfo. Also assertions need to take into account that more (or less) buckets are expected depending on minDocCount.
1 parent f70f54a commit feed66f

File tree

3 files changed

+74
-15
lines changed

3 files changed

+74
-15
lines changed

server/src/main/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogram.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ public int hashCode() {
219219
private final boolean keyed;
220220
private final long minDocCount;
221221
private final long offset;
222-
private final EmptyBucketInfo emptyBucketInfo;
222+
final EmptyBucketInfo emptyBucketInfo;
223223

224224
InternalDateHistogram(String name, List<Bucket> buckets, BucketOrder order, long minDocCount, long offset,
225225
EmptyBucketInfo emptyBucketInfo,

server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalDateHistogramTests.java

Lines changed: 72 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,12 +20,14 @@
2020
package org.elasticsearch.search.aggregations.bucket.histogram;
2121

2222
import org.elasticsearch.common.io.stream.Writeable;
23+
import org.elasticsearch.common.rounding.Rounding;
24+
import org.elasticsearch.common.unit.TimeValue;
2325
import org.elasticsearch.search.DocValueFormat;
2426
import org.elasticsearch.search.aggregations.BucketOrder;
2527
import org.elasticsearch.search.aggregations.InternalAggregations;
26-
import org.elasticsearch.test.InternalMultiBucketAggregationTestCase;
2728
import org.elasticsearch.search.aggregations.ParsedMultiBucketAggregation;
2829
import org.elasticsearch.search.aggregations.pipeline.PipelineAggregator;
30+
import org.elasticsearch.test.InternalMultiBucketAggregationTestCase;
2931
import org.joda.time.DateTime;
3032

3133
import java.util.ArrayList;
@@ -42,12 +44,38 @@ public class InternalDateHistogramTests extends InternalMultiBucketAggregationTe
4244

4345
private boolean keyed;
4446
private DocValueFormat format;
47+
private long intervalMillis;
48+
private long baseMillis;
49+
private long minDocCount;
50+
private InternalDateHistogram.EmptyBucketInfo emptyBucketInfo;
4551

4652
@Override
4753
public void setUp() throws Exception {
4854
super.setUp();
4955
keyed = randomBoolean();
5056
format = randomNumericDocValueFormat();
57+
//in order for reduction to work properly (and be realistic) we need to use the same interval, minDocCount, emptyBucketInfo
58+
//and base in all randomly created aggs as part of the same test run. This is particularly important when minDocCount is
59+
//set to 0 as empty buckets need to be added to fill the holes.
60+
long interval = randomIntBetween(1, 3);
61+
intervalMillis = randomFrom(timeValueSeconds(interval), timeValueMinutes(interval), timeValueHours(interval)).getMillis();
62+
Rounding rounding = Rounding.builder(TimeValue.timeValueMillis(intervalMillis)).build();
63+
baseMillis = rounding.round(System.currentTimeMillis());
64+
if (randomBoolean()) {
65+
minDocCount = randomIntBetween(1, 10);
66+
emptyBucketInfo = null;
67+
} else {
68+
minDocCount = 0;
69+
ExtendedBounds extendedBounds = null;
70+
if (randomBoolean()) {
71+
//it's ok if min and max are outside the range of the generated buckets, that will just mean that
72+
//empty buckets won't be added before the first bucket and/or after the last one
73+
long min = baseMillis - intervalMillis * randomNumberOfBuckets();
74+
long max = baseMillis + randomNumberOfBuckets() * intervalMillis + randomNumberOfBuckets();
75+
extendedBounds = new ExtendedBounds(min, max);
76+
}
77+
emptyBucketInfo = new InternalDateHistogram.EmptyBucketInfo(rounding, InternalAggregations.EMPTY, extendedBounds);
78+
}
5179
}
5280

5381
@Override
@@ -57,29 +85,58 @@ protected InternalDateHistogram createTestInstance(String name,
5785
InternalAggregations aggregations) {
5886
int nbBuckets = randomNumberOfBuckets();
5987
List<InternalDateHistogram.Bucket> buckets = new ArrayList<>(nbBuckets);
60-
long startingDate = System.currentTimeMillis();
61-
62-
long interval = randomIntBetween(1, 3);
63-
long intervalMillis = randomFrom(timeValueSeconds(interval), timeValueMinutes(interval), timeValueHours(interval)).getMillis();
64-
88+
//avoid having different random instance start from exactly the same base
89+
long startingDate = baseMillis - intervalMillis * randomIntBetween(0, 100);
6590
for (int i = 0; i < nbBuckets; i++) {
66-
long key = startingDate + (intervalMillis * i);
67-
buckets.add(i, new InternalDateHistogram.Bucket(key, randomIntBetween(1, 100), keyed, format, aggregations));
91+
//rarely leave some holes to be filled up with empty buckets in case minDocCount is set to 0
92+
if (frequently()) {
93+
long key = startingDate + intervalMillis * i;
94+
buckets.add(new InternalDateHistogram.Bucket(key, randomIntBetween(1, 100), keyed, format, aggregations));
95+
}
6896
}
69-
70-
BucketOrder order = randomFrom(BucketOrder.key(true), BucketOrder.key(false));
71-
return new InternalDateHistogram(name, buckets, order, 1, 0L, null, format, keyed, pipelineAggregators, metaData);
97+
BucketOrder order = BucketOrder.key(randomBoolean());
98+
return new InternalDateHistogram(name, buckets, order, minDocCount, 0L, emptyBucketInfo, format, keyed,
99+
pipelineAggregators, metaData);
72100
}
73101

74102
@Override
75103
protected void assertReduced(InternalDateHistogram reduced, List<InternalDateHistogram> inputs) {
76-
Map<Long, Long> expectedCounts = new TreeMap<>();
104+
TreeMap<Long, Long> expectedCounts = new TreeMap<>();
77105
for (Histogram histogram : inputs) {
78106
for (Histogram.Bucket bucket : histogram.getBuckets()) {
79107
expectedCounts.compute(((DateTime) bucket.getKey()).getMillis(),
80108
(key, oldValue) -> (oldValue == null ? 0 : oldValue) + bucket.getDocCount());
81109
}
82110
}
111+
if (minDocCount == 0) {
112+
long minBound = -1;
113+
long maxBound = -1;
114+
if (emptyBucketInfo.bounds != null) {
115+
minBound = emptyBucketInfo.rounding.round(emptyBucketInfo.bounds.getMin());
116+
maxBound = emptyBucketInfo.rounding.round(emptyBucketInfo.bounds.getMax());
117+
if (expectedCounts.isEmpty() && minBound <= maxBound) {
118+
expectedCounts.put(minBound, 0L);
119+
}
120+
}
121+
if (expectedCounts.isEmpty() == false) {
122+
Long nextKey = expectedCounts.firstKey();
123+
while (nextKey < expectedCounts.lastKey()) {
124+
expectedCounts.putIfAbsent(nextKey, 0L);
125+
nextKey += intervalMillis;
126+
}
127+
if (emptyBucketInfo.bounds != null) {
128+
while (minBound < expectedCounts.firstKey()) {
129+
expectedCounts.put(expectedCounts.firstKey() - intervalMillis, 0L);
130+
}
131+
while (expectedCounts.lastKey() < maxBound) {
132+
expectedCounts.put(expectedCounts.lastKey() + intervalMillis, 0L);
133+
}
134+
}
135+
}
136+
} else {
137+
expectedCounts.entrySet().removeIf(doubleLongEntry -> doubleLongEntry.getValue() < minDocCount);
138+
}
139+
83140
Map<Long, Long> actualCounts = new TreeMap<>();
84141
for (Histogram.Bucket bucket : reduced.getBuckets()) {
85142
actualCounts.compute(((DateTime) bucket.getKey()).getMillis(),
@@ -106,6 +163,7 @@ protected InternalDateHistogram mutateInstance(InternalDateHistogram instance) {
106163
long minDocCount = instance.getMinDocCount();
107164
long offset = instance.getOffset();
108165
List<PipelineAggregator> pipelineAggregators = instance.pipelineAggregators();
166+
InternalDateHistogram.EmptyBucketInfo emptyBucketInfo = instance.emptyBucketInfo;
109167
Map<String, Object> metaData = instance.getMetaData();
110168
switch (between(0, 5)) {
111169
case 0:
@@ -121,6 +179,7 @@ protected InternalDateHistogram mutateInstance(InternalDateHistogram instance) {
121179
break;
122180
case 3:
123181
minDocCount += between(1, 10);
182+
emptyBucketInfo = null;
124183
break;
125184
case 4:
126185
offset += between(1, 20);
@@ -136,7 +195,7 @@ protected InternalDateHistogram mutateInstance(InternalDateHistogram instance) {
136195
default:
137196
throw new AssertionError("Illegal randomisation branch");
138197
}
139-
return new InternalDateHistogram(name, buckets, order, minDocCount, offset, null, format, keyed, pipelineAggregators,
198+
return new InternalDateHistogram(name, buckets, order, minDocCount, offset, emptyBucketInfo, format, keyed, pipelineAggregators,
140199
metaData);
141200
}
142201
}

server/src/test/java/org/elasticsearch/search/aggregations/bucket/histogram/InternalHistogramTests.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,7 @@ protected void assertReduced(InternalHistogram reduced, List<InternalHistogram>
123123
}
124124
if (minDocCount == 0) {
125125
double minBound = round(emptyBucketInfo.minBound);
126-
if (expectedCounts.isEmpty() && emptyBucketInfo.minBound < emptyBucketInfo.maxBound) {
126+
if (expectedCounts.isEmpty() && emptyBucketInfo.minBound <= emptyBucketInfo.maxBound) {
127127
expectedCounts.put(minBound, 0L);
128128
}
129129
if (expectedCounts.isEmpty() == false) {

0 commit comments

Comments
 (0)