Skip to content

Commit becf069

Browse files
authored
Esql refactor date tests (#117923)
This refactors a bit of the type logic in the parametrized testing to pass the input values as java Instants for millisecond and nanosecond date. Mainly, this impacts verifier functions. The goal here is to ensure that the values are correctly converted based on the type they were generated as, rather than relying on the verifier function to know how to convert from a long with no additional information. This will make tests that have mixed millisecond and nanosecond inputs easier to write correctly.
1 parent ebf470a commit becf069

File tree

15 files changed

+117
-163
lines changed

15 files changed

+117
-163
lines changed

x-pack/plugin/esql-core/src/main/java/org/elasticsearch/xpack/esql/core/util/DateUtils.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,10 @@ public static ZonedDateTime asDateTime(long millis) {
174174
return ZonedDateTime.ofInstant(Instant.ofEpochMilli(millis), UTC);
175175
}
176176

177+
public static ZonedDateTime asDateTime(Instant instant) {
178+
return ZonedDateTime.ofInstant(instant, UTC);
179+
}
180+
177181
public static long asMillis(ZonedDateTime zonedDateTime) {
178182
return zonedDateTime.toInstant().toEpochMilli();
179183
}

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/TestCaseSupplier.java

Lines changed: 11 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -620,70 +620,6 @@ public static void forUnaryBoolean(
620620
unary(suppliers, expectedEvaluatorToString, booleanCases(), expectedType, v -> expectedValue.apply((Boolean) v), warnings);
621621
}
622622

623-
/**
624-
* Generate positive test cases for a unary function operating on an {@link DataType#DATETIME}.
625-
* This variant defaults to maximum range of possible values
626-
*/
627-
public static void forUnaryDatetime(
628-
List<TestCaseSupplier> suppliers,
629-
String expectedEvaluatorToString,
630-
DataType expectedType,
631-
Function<Instant, Object> expectedValue,
632-
List<String> warnings
633-
) {
634-
unaryNumeric(
635-
suppliers,
636-
expectedEvaluatorToString,
637-
dateCases(),
638-
expectedType,
639-
n -> expectedValue.apply(Instant.ofEpochMilli(n.longValue())),
640-
warnings
641-
);
642-
}
643-
644-
/**
645-
* Generate positive test cases for a unary function operating on an {@link DataType#DATETIME}.
646-
* This variant accepts a range of values
647-
*/
648-
public static void forUnaryDatetime(
649-
List<TestCaseSupplier> suppliers,
650-
String expectedEvaluatorToString,
651-
DataType expectedType,
652-
long min,
653-
long max,
654-
Function<Instant, Object> expectedValue,
655-
List<String> warnings
656-
) {
657-
unaryNumeric(
658-
suppliers,
659-
expectedEvaluatorToString,
660-
dateCases(min, max),
661-
expectedType,
662-
n -> expectedValue.apply(Instant.ofEpochMilli(n.longValue())),
663-
warnings
664-
);
665-
}
666-
667-
/**
668-
* Generate positive test cases for a unary function operating on an {@link DataType#DATE_NANOS}.
669-
*/
670-
public static void forUnaryDateNanos(
671-
List<TestCaseSupplier> suppliers,
672-
String expectedEvaluatorToString,
673-
DataType expectedType,
674-
Function<Instant, Object> expectedValue,
675-
List<String> warnings
676-
) {
677-
unaryNumeric(
678-
suppliers,
679-
expectedEvaluatorToString,
680-
dateNanosCases(),
681-
expectedType,
682-
n -> expectedValue.apply(DateUtils.toInstant((long) n)),
683-
warnings
684-
);
685-
}
686-
687623
/**
688624
* Generate positive test cases for a unary function operating on an {@link DataType#GEO_POINT}.
689625
*/
@@ -1912,11 +1848,19 @@ public List<Object> multiRowData() {
19121848
}
19131849

19141850
/**
1915-
* @return the data value being supplied, casting unsigned longs into BigIntegers correctly
1851+
* @return the data value being supplied, casting to java objects when appropriate
19161852
*/
19171853
public Object getValue() {
1918-
if (type == DataType.UNSIGNED_LONG && data instanceof Long l) {
1919-
return NumericUtils.unsignedLongAsBigInteger(l);
1854+
if (data instanceof Long l) {
1855+
if (type == DataType.UNSIGNED_LONG) {
1856+
return NumericUtils.unsignedLongAsBigInteger(l);
1857+
}
1858+
if (type == DataType.DATETIME) {
1859+
return Instant.ofEpochMilli(l);
1860+
}
1861+
if (type == DataType.DATE_NANOS) {
1862+
return DateUtils.toInstant(l);
1863+
}
19201864
}
19211865
return data;
19221866
}

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDateNanosTests.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
2020

2121
import java.math.BigInteger;
22+
import java.time.Instant;
2223
import java.util.ArrayList;
2324
import java.util.List;
2425
import java.util.function.Supplier;
@@ -36,14 +37,20 @@ public static Iterable<Object[]> parameters() {
3637
final String read = "Attribute[channel=0]";
3738
final List<TestCaseSupplier> suppliers = new ArrayList<>();
3839

39-
TestCaseSupplier.forUnaryDateNanos(suppliers, read, DataType.DATE_NANOS, DateUtils::toLong, List.of());
40-
TestCaseSupplier.forUnaryDatetime(
40+
TestCaseSupplier.unary(
41+
suppliers,
42+
read,
43+
TestCaseSupplier.dateNanosCases(),
44+
DataType.DATE_NANOS,
45+
v -> DateUtils.toLong((Instant) v),
46+
List.of()
47+
);
48+
TestCaseSupplier.unary(
4149
suppliers,
4250
"ToDateNanosFromDatetimeEvaluator[field=" + read + "]",
51+
TestCaseSupplier.dateCases(0, DateUtils.MAX_NANOSECOND_INSTANT.toEpochMilli()),
4352
DataType.DATE_NANOS,
44-
0,
45-
DateUtils.MAX_NANOSECOND_INSTANT.toEpochMilli(),
46-
i -> DateUtils.toNanoSeconds(i.toEpochMilli()),
53+
i -> DateUtils.toNanoSeconds(((Instant) i).toEpochMilli()),
4754
List.of()
4855
);
4956
TestCaseSupplier.forUnaryLong(

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDatetimeTests.java

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,20 @@ public static Iterable<Object[]> parameters() {
3737
final String read = "Attribute[channel=0]";
3838
final List<TestCaseSupplier> suppliers = new ArrayList<>();
3939

40-
TestCaseSupplier.forUnaryDatetime(suppliers, read, DataType.DATETIME, Instant::toEpochMilli, emptyList());
41-
TestCaseSupplier.forUnaryDateNanos(
40+
TestCaseSupplier.unary(
41+
suppliers,
42+
read,
43+
TestCaseSupplier.dateCases(),
44+
DataType.DATETIME,
45+
v -> ((Instant) v).toEpochMilli(),
46+
emptyList()
47+
);
48+
TestCaseSupplier.unary(
4249
suppliers,
4350
"ToDatetimeFromDateNanosEvaluator[field=" + read + "]",
51+
TestCaseSupplier.dateNanosCases(),
4452
DataType.DATETIME,
45-
i -> DateUtils.toMilliSeconds(DateUtils.toLong(i)),
53+
i -> DateUtils.toMilliSeconds(DateUtils.toLong((Instant) i)),
4654
emptyList()
4755
);
4856

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToDoubleTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.elasticsearch.xpack.esql.type.EsqlDataTypeConverter;
2222

2323
import java.math.BigInteger;
24+
import java.time.Instant;
2425
import java.util.ArrayList;
2526
import java.util.List;
2627
import java.util.function.Function;
@@ -49,11 +50,12 @@ public static Iterable<Object[]> parameters() {
4950
);
5051

5152
TestCaseSupplier.forUnaryBoolean(suppliers, evaluatorName.apply("Boolean"), DataType.DOUBLE, b -> b ? 1d : 0d, List.of());
52-
TestCaseSupplier.forUnaryDatetime(
53+
TestCaseSupplier.unary(
5354
suppliers,
5455
evaluatorName.apply("Long"),
56+
TestCaseSupplier.dateCases(),
5557
DataType.DOUBLE,
56-
i -> (double) i.toEpochMilli(),
58+
i -> (double) ((Instant) i).toEpochMilli(),
5759
List.of()
5860
);
5961
// random strings that don't look like a double

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToIntegerTests.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
2020

2121
import java.math.BigInteger;
22+
import java.time.Instant;
2223
import java.util.ArrayList;
2324
import java.util.List;
2425
import java.util.function.Function;
@@ -48,7 +49,7 @@ public static Iterable<Object[]> parameters() {
4849
evaluatorName.apply("Long"),
4950
dateCases(0, Integer.MAX_VALUE),
5051
DataType.INTEGER,
51-
l -> ((Long) l).intValue(),
52+
l -> Long.valueOf(((Instant) l).toEpochMilli()).intValue(),
5253
List.of()
5354
);
5455
// datetimes that fall outside Integer's range
@@ -60,7 +61,9 @@ public static Iterable<Object[]> parameters() {
6061
l -> null,
6162
l -> List.of(
6263
"Line -1:-1: evaluation of [] failed, treating result as null. Only first 20 failures recorded.",
63-
"Line -1:-1: org.elasticsearch.xpack.esql.core.InvalidArgumentException: [" + l + "] out of [integer] range"
64+
"Line -1:-1: org.elasticsearch.xpack.esql.core.InvalidArgumentException: ["
65+
+ ((Instant) l).toEpochMilli()
66+
+ "] out of [integer] range"
6467
)
6568
);
6669
// random strings that don't look like an Integer

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToLongTests.java

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,15 @@ public static Iterable<Object[]> parameters() {
4343
TestCaseSupplier.forUnaryBoolean(suppliers, evaluatorName.apply("Boolean"), DataType.LONG, b -> b ? 1L : 0L, List.of());
4444

4545
// datetimes
46-
TestCaseSupplier.forUnaryDatetime(suppliers, read, DataType.LONG, Instant::toEpochMilli, List.of());
47-
TestCaseSupplier.forUnaryDateNanos(suppliers, read, DataType.LONG, DateUtils::toLong, List.of());
46+
TestCaseSupplier.unary(suppliers, read, TestCaseSupplier.dateCases(), DataType.LONG, v -> ((Instant) v).toEpochMilli(), List.of());
47+
TestCaseSupplier.unary(
48+
suppliers,
49+
read,
50+
TestCaseSupplier.dateNanosCases(),
51+
DataType.LONG,
52+
v -> DateUtils.toLong((Instant) v),
53+
List.of()
54+
);
4855
// random strings that don't look like a long
4956
TestCaseSupplier.forUnaryStrings(
5057
suppliers,

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToStringTests.java

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier;
2222

2323
import java.math.BigInteger;
24+
import java.time.Instant;
2425
import java.util.ArrayList;
2526
import java.util.List;
2627
import java.util.function.Supplier;
@@ -81,18 +82,20 @@ public static Iterable<Object[]> parameters() {
8182
b -> new BytesRef(b.toString()),
8283
List.of()
8384
);
84-
TestCaseSupplier.forUnaryDatetime(
85+
TestCaseSupplier.unary(
8586
suppliers,
8687
"ToStringFromDatetimeEvaluator[field=" + read + "]",
88+
TestCaseSupplier.dateCases(),
8789
DataType.KEYWORD,
88-
i -> new BytesRef(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.formatMillis(i.toEpochMilli())),
90+
i -> new BytesRef(DateFieldMapper.DEFAULT_DATE_TIME_FORMATTER.formatMillis(((Instant) i).toEpochMilli())),
8991
List.of()
9092
);
91-
TestCaseSupplier.forUnaryDateNanos(
93+
TestCaseSupplier.unary(
9294
suppliers,
9395
"ToStringFromDateNanosEvaluator[field=" + read + "]",
96+
TestCaseSupplier.dateNanosCases(),
9497
DataType.KEYWORD,
95-
i -> new BytesRef(DateFieldMapper.DEFAULT_DATE_TIME_NANOS_FORMATTER.formatNanos(DateUtils.toLong(i))),
98+
i -> new BytesRef(DateFieldMapper.DEFAULT_DATE_TIME_NANOS_FORMATTER.formatNanos(DateUtils.toLong((Instant) i))),
9699
List.of()
97100
);
98101
TestCaseSupplier.forUnaryGeoPoint(

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/convert/ToUnsignedLongTests.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919

2020
import java.math.BigDecimal;
2121
import java.math.BigInteger;
22+
import java.time.Instant;
2223
import java.util.ArrayList;
2324
import java.util.List;
2425
import java.util.function.Function;
@@ -58,11 +59,12 @@ public static Iterable<Object[]> parameters() {
5859
);
5960

6061
// datetimes
61-
TestCaseSupplier.forUnaryDatetime(
62+
TestCaseSupplier.unary(
6263
suppliers,
6364
evaluatorName.apply("Long"),
65+
TestCaseSupplier.dateCases(),
6466
DataType.UNSIGNED_LONG,
65-
instant -> BigInteger.valueOf(instant.toEpochMilli()),
67+
instant -> BigInteger.valueOf(((Instant) instant).toEpochMilli()),
6668
List.of()
6769
);
6870
// random strings that don't look like an unsigned_long

x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/predicate/operator/arithmetic/AddTests.java

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ public static Iterable<Object[]> parameters() {
159159
};
160160
BiFunction<TestCaseSupplier.TypedData, TestCaseSupplier.TypedData, List<String>> warnings = (lhs, rhs) -> {
161161
try {
162-
addDatesAndTemporalAmount(lhs.data(), rhs.data(), AddTests::addMillis);
162+
addDatesAndTemporalAmount(lhs.getValue(), rhs.getValue(), AddTests::addMillis);
163163
return List.of();
164164
} catch (ArithmeticException e) {
165165
return List.of(
@@ -193,6 +193,7 @@ public static Iterable<Object[]> parameters() {
193193

194194
BinaryOperator<Object> nanosResult = (lhs, rhs) -> {
195195
try {
196+
assert (lhs instanceof Instant) || (rhs instanceof Instant);
196197
return addDatesAndTemporalAmount(lhs, rhs, AddTests::addNanos);
197198
} catch (ArithmeticException e) {
198199
return null;
@@ -327,29 +328,28 @@ private static String addErrorMessageString(boolean includeOrdinal, List<Set<Dat
327328
}
328329
}
329330

330-
private static Object addDatesAndTemporalAmount(Object lhs, Object rhs, ToLongBiFunction<Long, TemporalAmount> adder) {
331+
private static Object addDatesAndTemporalAmount(Object lhs, Object rhs, ToLongBiFunction<Instant, TemporalAmount> adder) {
331332
// this weird casting dance makes the expected value lambda symmetric
332-
Long date;
333+
Instant date;
333334
TemporalAmount period;
334-
if (lhs instanceof Long) {
335-
date = (Long) lhs;
335+
assert (lhs instanceof Instant) || (rhs instanceof Instant);
336+
if (lhs instanceof Instant) {
337+
date = (Instant) lhs;
336338
period = (TemporalAmount) rhs;
337339
} else {
338-
date = (Long) rhs;
340+
date = (Instant) rhs;
339341
period = (TemporalAmount) lhs;
340342
}
341343
return adder.applyAsLong(date, period);
342344
}
343345

344-
private static long addMillis(Long date, TemporalAmount period) {
346+
private static long addMillis(Instant date, TemporalAmount period) {
345347
return asMillis(asDateTime(date).plus(period));
346348
}
347349

348-
private static long addNanos(Long date, TemporalAmount period) {
350+
private static long addNanos(Instant date, TemporalAmount period) {
349351
return DateUtils.toLong(
350-
Instant.from(
351-
ZonedDateTime.ofInstant(DateUtils.toInstant(date), org.elasticsearch.xpack.esql.core.util.DateUtils.UTC).plus(period)
352-
)
352+
Instant.from(ZonedDateTime.ofInstant(date, org.elasticsearch.xpack.esql.core.util.DateUtils.UTC).plus(period))
353353
);
354354
}
355355

0 commit comments

Comments
 (0)