Skip to content

Commit 860b86b

Browse files
authored
ESQL: Make function tests with timezone or locale use random configurations (#138107)
Closes #107997 Since configurations were fully randomized, some tests requiring specific locales or timezones were changed to use a static config. In this PR, those tests were updated to still use a random configuration, but override the single parameter they need to be static as part of the test case.
1 parent c086b07 commit 860b86b

File tree

13 files changed

+248
-161
lines changed

13 files changed

+248
-161
lines changed

test/framework/src/main/java/org/elasticsearch/test/ReadableMatchers.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
package org.elasticsearch.test;
1111

12+
import org.apache.lucene.util.BytesRef;
1213
import org.elasticsearch.common.time.DateFormatter;
1314
import org.elasticsearch.common.time.DateUtils;
1415
import org.hamcrest.Description;
@@ -39,6 +40,13 @@ public static DateNanosMatcher matchesDateNanos(String date) {
3940
return new DateNanosMatcher(date);
4041
}
4142

43+
/**
44+
* Test matcher for BytesRef that expects BytesRefs, but describes the errors as strings, for better readability.
45+
*/
46+
public static StringBytesRefMatcher matchesBytesRef(String string) {
47+
return new StringBytesRefMatcher(string);
48+
}
49+
4250
public static class DateMillisMatcher extends TypeSafeMatcher<Long> {
4351
private final long timeMillis;
4452

@@ -84,4 +92,30 @@ public void describeTo(Description description) {
8492
description.appendText(dateFormatter.formatNanos(timeNanos));
8593
}
8694
}
95+
96+
public static class StringBytesRefMatcher extends TypeSafeMatcher<BytesRef> {
97+
private final String string;
98+
private final BytesRef bytesRef;
99+
100+
public StringBytesRefMatcher(String string) {
101+
this.string = string;
102+
this.bytesRef = new BytesRef(string);
103+
}
104+
105+
@Override
106+
protected boolean matchesSafely(BytesRef item) {
107+
return item.equals(bytesRef);
108+
}
109+
110+
@Override
111+
public void describeMismatchSafely(BytesRef item, Description description) {
112+
description.appendText("was ").appendValue(item.utf8ToString());
113+
}
114+
115+
@Override
116+
public void describeTo(Description description) {
117+
description.appendText(string);
118+
}
119+
}
120+
87121
}

x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/ConfigurationTestUtils.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ public static Configuration randomConfiguration(String query, Map<String, Map<St
8080
);
8181
}
8282

83+
public static ConfigurationBuilder randomConfigurationBuilder() {
84+
return new ConfigurationBuilder(randomConfiguration());
85+
}
86+
8387
private static QueryPragmas randomQueryPragmas() {
8488
return new QueryPragmas(
8589
Settings.builder().put(QueryPragmas.DATA_PARTITIONING.getKey(), randomFrom(DataPartitioning.values())).build()

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

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,7 +1606,7 @@ public static List<TestCaseSupplier> mapTestCases(
16061606
) {
16071607
return suppliers.stream()
16081608
.map(supplier -> new TestCaseSupplier(supplier.name(), supplier.types(), () -> mapper.apply(supplier.get())))
1609-
.toList();
1609+
.collect(Collectors.toCollection(ArrayList::new));
16101610
}
16111611

16121612
public static final class TestCase {
@@ -1822,19 +1822,6 @@ public Object extra() {
18221822
return extra;
18231823
}
18241824

1825-
/**
1826-
* Build a new {@link TestCase} with the {@link #TEST_CONFIGURATION}.
1827-
* <p>
1828-
* The source is also set to match the configuration
1829-
* </p>
1830-
*
1831-
* @deprecated Use a custom configuration instead, and test the results.
1832-
*/
1833-
@Deprecated
1834-
public TestCase withStaticConfiguration() {
1835-
return withConfiguration(TEST_SOURCE, TEST_CONFIGURATION);
1836-
}
1837-
18381825
/**
18391826
* Build a new {@link TestCase} with new {@link #configuration}.
18401827
* <p>

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

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
import java.time.Duration;
3131
import java.time.Instant;
3232
import java.time.Period;
33+
import java.time.ZoneOffset;
3334
import java.util.ArrayList;
3435
import java.util.List;
3536
import java.util.function.LongSupplier;
@@ -124,7 +125,7 @@ private static void dateCases(List<TestCaseSupplier> suppliers, String name, Lon
124125
+ "rounding=Rounding[DAY_OF_MONTH in Z][fixed to midnight]]",
125126
DataType.DATETIME,
126127
resultsMatcher(args)
127-
).withStaticConfiguration();
128+
).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC));
128129
}));
129130
// same as above, but a low bucket count and datetime bounds that match it (at hour span)
130131
suppliers.add(new TestCaseSupplier(name, List.of(DataType.DATETIME, DataType.INTEGER, fromType, toType), () -> {
@@ -138,7 +139,7 @@ private static void dateCases(List<TestCaseSupplier> suppliers, String name, Lon
138139
"DateTruncDatetimeEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding[3600000 in Z][fixed]]",
139140
DataType.DATETIME,
140141
equalTo(Rounding.builder(Rounding.DateTimeUnit.HOUR_OF_DAY).build().prepareForUnknown().round(date.getAsLong()))
141-
).withStaticConfiguration();
142+
).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC));
142143
}));
143144
}
144145
}
@@ -253,7 +254,7 @@ private static void dateCasesWithSpan(
253254
"DateTruncDatetimeEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding" + spanStr + "]",
254255
DataType.DATETIME,
255256
resultsMatcher(args)
256-
).withStaticConfiguration();
257+
).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC));
257258
}));
258259
}
259260

@@ -274,7 +275,7 @@ private static void dateNanosCasesWithSpan(
274275
Matchers.startsWith("DateTruncDateNanosEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding["),
275276
DataType.DATE_NANOS,
276277
resultsMatcher(args)
277-
).withStaticConfiguration();
278+
).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC));
278279
}));
279280
}
280281

@@ -293,7 +294,7 @@ private static void dateNanosCases(List<TestCaseSupplier> suppliers, String name
293294
Matchers.startsWith("DateTruncDateNanosEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding["),
294295
DataType.DATE_NANOS,
295296
resultsMatcher(args)
296-
).withStaticConfiguration();
297+
).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC));
297298
}));
298299
// same as above, but a low bucket count and datetime bounds that match it (at hour span)
299300
suppliers.add(new TestCaseSupplier(name, List.of(DataType.DATE_NANOS, DataType.INTEGER, fromType, toType), () -> {
@@ -307,7 +308,7 @@ private static void dateNanosCases(List<TestCaseSupplier> suppliers, String name
307308
Matchers.startsWith("DateTruncDateNanosEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding["),
308309
DataType.DATE_NANOS,
309310
equalTo(Rounding.builder(Rounding.DateTimeUnit.HOUR_OF_DAY).build().prepareForUnknown().round(date.getAsLong()))
310-
).withStaticConfiguration();
311+
).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC));
311312
}));
312313
}
313314
}

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.time.Duration;
3030
import java.time.Instant;
3131
import java.time.Period;
32+
import java.time.ZoneOffset;
3233
import java.util.ArrayList;
3334
import java.util.List;
3435
import java.util.function.LongSupplier;
@@ -105,7 +106,7 @@ private static void dateCasesWithSpan(
105106
"DateTruncDatetimeEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding" + spanStr + "]",
106107
DataType.DATETIME,
107108
resultsMatcher(args)
108-
).withStaticConfiguration();
109+
).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC));
109110
}));
110111
}
111112

@@ -125,7 +126,7 @@ private static void dateNanosCasesWithSpan(
125126
Matchers.startsWith("DateTruncDateNanosEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding["),
126127
DataType.DATE_NANOS,
127128
resultsMatcher(args)
128-
).withStaticConfiguration();
129+
).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC));
129130
}));
130131
}
131132

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

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,16 +7,17 @@
77

88
package org.elasticsearch.xpack.esql.expression.function.scalar;
99

10-
import org.elasticsearch.xpack.esql.ConfigurationBuilder;
1110
import org.elasticsearch.xpack.esql.core.expression.Expression;
1211
import org.elasticsearch.xpack.esql.core.tree.Source;
1312
import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase;
1413
import org.elasticsearch.xpack.esql.session.Configuration;
1514

1615
import java.time.ZoneId;
1716
import java.util.List;
17+
import java.util.Locale;
1818

1919
import static org.elasticsearch.xpack.esql.ConfigurationTestUtils.randomConfiguration;
20+
import static org.elasticsearch.xpack.esql.ConfigurationTestUtils.randomConfigurationBuilder;
2021
import static org.elasticsearch.xpack.esql.ConfigurationTestUtils.randomTables;
2122
import static org.elasticsearch.xpack.esql.SerializationTestUtils.assertSerialization;
2223
import static org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier.TEST_SOURCE;
@@ -48,6 +49,14 @@ public void testSerializationWithConfiguration() {
4849
}
4950

5051
protected static Configuration configurationForTimezone(ZoneId zoneId) {
51-
return new ConfigurationBuilder(randomConfiguration()).query(TEST_SOURCE.text()).zoneId(zoneId).build();
52+
return randomConfigurationBuilder().query(TEST_SOURCE.text()).zoneId(zoneId).build();
53+
}
54+
55+
protected static Configuration configurationForLocale(Locale locale) {
56+
return randomConfigurationBuilder().query(TEST_SOURCE.text()).locale(locale).build();
57+
}
58+
59+
protected static Configuration configurationForTimezoneAndLocale(ZoneId zoneId, Locale locale) {
60+
return randomConfigurationBuilder().query(TEST_SOURCE.text()).zoneId(zoneId).locale(locale).build();
5261
}
5362
}

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

Lines changed: 43 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
1212

1313
import org.apache.lucene.util.BytesRef;
14+
import org.elasticsearch.common.time.DateUtils;
1415
import org.elasticsearch.compute.operator.DriverContext;
1516
import org.elasticsearch.xpack.esql.EsqlTestUtils;
1617
import org.elasticsearch.xpack.esql.core.InvalidArgumentException;
@@ -24,12 +25,15 @@
2425
import org.elasticsearch.xpack.esql.session.Configuration;
2526

2627
import java.time.Instant;
28+
import java.time.ZoneId;
29+
import java.time.ZoneOffset;
2730
import java.time.ZonedDateTime;
2831
import java.time.temporal.ChronoField;
2932
import java.util.ArrayList;
3033
import java.util.List;
3134
import java.util.function.Supplier;
3235

36+
import static org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier.TEST_SOURCE;
3337
import static org.hamcrest.Matchers.equalTo;
3438
import static org.hamcrest.Matchers.is;
3539
import static org.hamcrest.Matchers.nullValue;
@@ -46,30 +50,6 @@ public static Iterable<Object[]> parameters() {
4650
for (var stringType : DataType.stringTypes()) {
4751
suppliers.addAll(
4852
List.of(
49-
new TestCaseSupplier(
50-
List.of(stringType, DataType.DATETIME),
51-
() -> new TestCaseSupplier.TestCase(
52-
List.of(
53-
new TestCaseSupplier.TypedData(new BytesRef("YeAr"), stringType, "chrono"),
54-
new TestCaseSupplier.TypedData(1687944333000L, DataType.DATETIME, "date")
55-
),
56-
"DateExtractMillisEvaluator[value=Attribute[channel=1], chronoField=Attribute[channel=0], zone=Z]",
57-
DataType.LONG,
58-
equalTo(2023L)
59-
).withStaticConfiguration()
60-
),
61-
new TestCaseSupplier(
62-
List.of(stringType, DataType.DATE_NANOS),
63-
() -> new TestCaseSupplier.TestCase(
64-
List.of(
65-
new TestCaseSupplier.TypedData(new BytesRef("YeAr"), stringType, "chrono"),
66-
new TestCaseSupplier.TypedData(1687944333000000000L, DataType.DATE_NANOS, "date")
67-
),
68-
"DateExtractNanosEvaluator[value=Attribute[channel=1], chronoField=Attribute[channel=0], zone=Z]",
69-
DataType.LONG,
70-
equalTo(2023L)
71-
).withStaticConfiguration()
72-
),
7353
new TestCaseSupplier(
7454
List.of(stringType, DataType.DATE_NANOS),
7555
() -> new TestCaseSupplier.TestCase(
@@ -80,7 +60,7 @@ public static Iterable<Object[]> parameters() {
8060
"DateExtractNanosEvaluator[value=Attribute[channel=1], chronoField=Attribute[channel=0], zone=Z]",
8161
DataType.LONG,
8262
equalTo(123456L)
83-
).withStaticConfiguration()
63+
).withConfiguration(TestCaseSupplier.TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC))
8464
),
8565
new TestCaseSupplier(
8666
List.of(stringType, DataType.DATETIME),
@@ -93,7 +73,7 @@ public static Iterable<Object[]> parameters() {
9373
"DateExtractMillisEvaluator[value=Attribute[channel=1], chronoField=Attribute[channel=0], zone=Z]",
9474
DataType.LONG,
9575
is(nullValue())
96-
).withStaticConfiguration()
76+
).withConfiguration(TestCaseSupplier.TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC))
9777
.withWarning(
9878
"Line 1:1: evaluation of [source] failed, treating result as null. Only first 20 failures recorded."
9979
)
@@ -107,9 +87,46 @@ public static Iterable<Object[]> parameters() {
10787
);
10888
}
10989

90+
suppliers.addAll(casesFor("YeAr", "2023-11-04T16:13:12Z", "Z", 2023));
91+
suppliers.addAll(casesFor("day_of_month", "2020-01-01T00:00:00Z", "America/New_York", 31));
92+
suppliers.addAll(casesFor("month_of_year", "2020-06-30T23:00:00Z", "Europe/Paris", 7));
93+
11094
return parameterSuppliersFromTypedDataWithDefaultChecks(true, suppliers);
11195
}
11296

97+
private static List<TestCaseSupplier> casesFor(String field, String date, String zoneIdName, long expectedResult) {
98+
long dateMillis = Instant.parse(date).toEpochMilli();
99+
ZoneId zoneId = ZoneId.of(zoneIdName);
100+
return List.of(
101+
new TestCaseSupplier(
102+
field + " - " + date + " (millis) - " + zoneId,
103+
List.of(DataType.KEYWORD, DataType.DATETIME),
104+
() -> new TestCaseSupplier.TestCase(
105+
List.of(
106+
new TestCaseSupplier.TypedData(field, DataType.KEYWORD, "field"),
107+
new TestCaseSupplier.TypedData(dateMillis, DataType.DATETIME, "date")
108+
),
109+
"DateExtractMillisEvaluator[value=Attribute[channel=1], chronoField=Attribute[channel=0], zone=" + zoneId + "]",
110+
DataType.LONG,
111+
equalTo(expectedResult)
112+
).withConfiguration(TEST_SOURCE, configurationForTimezone(zoneId))
113+
),
114+
new TestCaseSupplier(
115+
field + " - " + date + " (nanos) - " + zoneId,
116+
List.of(DataType.KEYWORD, DataType.DATE_NANOS),
117+
() -> new TestCaseSupplier.TestCase(
118+
List.of(
119+
new TestCaseSupplier.TypedData(field, DataType.KEYWORD, "field"),
120+
new TestCaseSupplier.TypedData(DateUtils.toNanoSeconds(dateMillis), DataType.DATE_NANOS, "date")
121+
),
122+
"DateExtractNanosEvaluator[value=Attribute[channel=1], chronoField=Attribute[channel=0], zone=" + zoneId + "]",
123+
DataType.LONG,
124+
equalTo(expectedResult)
125+
).withConfiguration(TEST_SOURCE, configurationForTimezone(zoneId))
126+
)
127+
);
128+
}
129+
113130
public void testAllChronoFields() {
114131
long epochMilli = 1687944333123L;
115132
ZonedDateTime date = Instant.ofEpochMilli(epochMilli).atZone(EsqlTestUtils.TEST_CFG.zoneId());

0 commit comments

Comments
 (0)