diff --git a/test/framework/src/main/java/org/elasticsearch/test/ReadableMatchers.java b/test/framework/src/main/java/org/elasticsearch/test/ReadableMatchers.java index b90187a8f04c8..775fce8f5dae8 100644 --- a/test/framework/src/main/java/org/elasticsearch/test/ReadableMatchers.java +++ b/test/framework/src/main/java/org/elasticsearch/test/ReadableMatchers.java @@ -9,6 +9,7 @@ package org.elasticsearch.test; +import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.time.DateFormatter; import org.elasticsearch.common.time.DateUtils; import org.hamcrest.Description; @@ -39,6 +40,13 @@ public static DateNanosMatcher matchesDateNanos(String date) { return new DateNanosMatcher(date); } + /** + * Test matcher for BytesRef that expects BytesRefs, but describes the errors as strings, for better readability. + */ + public static StringBytesRefMatcher matchesBytesRef(String string) { + return new StringBytesRefMatcher(string); + } + public static class DateMillisMatcher extends TypeSafeMatcher { private final long timeMillis; @@ -84,4 +92,30 @@ public void describeTo(Description description) { description.appendText(dateFormatter.formatNanos(timeNanos)); } } + + public static class StringBytesRefMatcher extends TypeSafeMatcher { + private final String string; + private final BytesRef bytesRef; + + public StringBytesRefMatcher(String string) { + this.string = string; + this.bytesRef = new BytesRef(string); + } + + @Override + protected boolean matchesSafely(BytesRef item) { + return item.equals(bytesRef); + } + + @Override + public void describeMismatchSafely(BytesRef item, Description description) { + description.appendText("was ").appendValue(item.utf8ToString()); + } + + @Override + public void describeTo(Description description) { + description.appendText(string); + } + } + } diff --git a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/ConfigurationTestUtils.java b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/ConfigurationTestUtils.java index ce59002fda037..af784827be003 100644 --- a/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/ConfigurationTestUtils.java +++ b/x-pack/plugin/esql/qa/testFixtures/src/main/java/org/elasticsearch/xpack/esql/ConfigurationTestUtils.java @@ -80,6 +80,10 @@ public static Configuration randomConfiguration(String query, Map mapTestCases( ) { return suppliers.stream() .map(supplier -> new TestCaseSupplier(supplier.name(), supplier.types(), () -> mapper.apply(supplier.get()))) - .toList(); + .collect(Collectors.toCollection(ArrayList::new)); } public static final class TestCase { @@ -1822,19 +1822,6 @@ public Object extra() { return extra; } - /** - * Build a new {@link TestCase} with the {@link #TEST_CONFIGURATION}. - *

- * The source is also set to match the configuration - *

- * - * @deprecated Use a custom configuration instead, and test the results. - */ - @Deprecated - public TestCase withStaticConfiguration() { - return withConfiguration(TEST_SOURCE, TEST_CONFIGURATION); - } - /** * Build a new {@link TestCase} with new {@link #configuration}. *

diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/grouping/BucketTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/grouping/BucketTests.java index 810e8a348d515..6a0757e8ba67d 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/grouping/BucketTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/grouping/BucketTests.java @@ -30,6 +30,7 @@ import java.time.Duration; import java.time.Instant; import java.time.Period; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; import java.util.function.LongSupplier; @@ -124,7 +125,7 @@ private static void dateCases(List suppliers, String name, Lon + "rounding=Rounding[DAY_OF_MONTH in Z][fixed to midnight]]", DataType.DATETIME, resultsMatcher(args) - ).withStaticConfiguration(); + ).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC)); })); // same as above, but a low bucket count and datetime bounds that match it (at hour span) suppliers.add(new TestCaseSupplier(name, List.of(DataType.DATETIME, DataType.INTEGER, fromType, toType), () -> { @@ -138,7 +139,7 @@ private static void dateCases(List suppliers, String name, Lon "DateTruncDatetimeEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding[3600000 in Z][fixed]]", DataType.DATETIME, equalTo(Rounding.builder(Rounding.DateTimeUnit.HOUR_OF_DAY).build().prepareForUnknown().round(date.getAsLong())) - ).withStaticConfiguration(); + ).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC)); })); } } @@ -253,7 +254,7 @@ private static void dateCasesWithSpan( "DateTruncDatetimeEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding" + spanStr + "]", DataType.DATETIME, resultsMatcher(args) - ).withStaticConfiguration(); + ).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC)); })); } @@ -274,7 +275,7 @@ private static void dateNanosCasesWithSpan( Matchers.startsWith("DateTruncDateNanosEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding["), DataType.DATE_NANOS, resultsMatcher(args) - ).withStaticConfiguration(); + ).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC)); })); } @@ -293,7 +294,7 @@ private static void dateNanosCases(List suppliers, String name Matchers.startsWith("DateTruncDateNanosEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding["), DataType.DATE_NANOS, resultsMatcher(args) - ).withStaticConfiguration(); + ).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC)); })); // same as above, but a low bucket count and datetime bounds that match it (at hour span) suppliers.add(new TestCaseSupplier(name, List.of(DataType.DATE_NANOS, DataType.INTEGER, fromType, toType), () -> { @@ -307,7 +308,7 @@ private static void dateNanosCases(List suppliers, String name Matchers.startsWith("DateTruncDateNanosEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding["), DataType.DATE_NANOS, equalTo(Rounding.builder(Rounding.DateTimeUnit.HOUR_OF_DAY).build().prepareForUnknown().round(date.getAsLong())) - ).withStaticConfiguration(); + ).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC)); })); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/grouping/TBucketTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/grouping/TBucketTests.java index 675f9ec9d2ecf..881652f097675 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/grouping/TBucketTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/grouping/TBucketTests.java @@ -29,6 +29,7 @@ import java.time.Duration; import java.time.Instant; import java.time.Period; +import java.time.ZoneOffset; import java.util.ArrayList; import java.util.List; import java.util.function.LongSupplier; @@ -105,7 +106,7 @@ private static void dateCasesWithSpan( "DateTruncDatetimeEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding" + spanStr + "]", DataType.DATETIME, resultsMatcher(args) - ).withStaticConfiguration(); + ).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC)); })); } @@ -125,7 +126,7 @@ private static void dateNanosCasesWithSpan( Matchers.startsWith("DateTruncDateNanosEvaluator[fieldVal=Attribute[channel=0], rounding=Rounding["), DataType.DATE_NANOS, resultsMatcher(args) - ).withStaticConfiguration(); + ).withConfiguration(TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC)); })); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/AbstractConfigurationFunctionTestCase.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/AbstractConfigurationFunctionTestCase.java index 5a677c076f5dc..8d6e26034202c 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/AbstractConfigurationFunctionTestCase.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/AbstractConfigurationFunctionTestCase.java @@ -7,7 +7,6 @@ package org.elasticsearch.xpack.esql.expression.function.scalar; -import org.elasticsearch.xpack.esql.ConfigurationBuilder; import org.elasticsearch.xpack.esql.core.expression.Expression; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.expression.function.AbstractScalarFunctionTestCase; @@ -15,8 +14,10 @@ import java.time.ZoneId; import java.util.List; +import java.util.Locale; import static org.elasticsearch.xpack.esql.ConfigurationTestUtils.randomConfiguration; +import static org.elasticsearch.xpack.esql.ConfigurationTestUtils.randomConfigurationBuilder; import static org.elasticsearch.xpack.esql.ConfigurationTestUtils.randomTables; import static org.elasticsearch.xpack.esql.SerializationTestUtils.assertSerialization; import static org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier.TEST_SOURCE; @@ -48,6 +49,14 @@ public void testSerializationWithConfiguration() { } protected static Configuration configurationForTimezone(ZoneId zoneId) { - return new ConfigurationBuilder(randomConfiguration()).query(TEST_SOURCE.text()).zoneId(zoneId).build(); + return randomConfigurationBuilder().query(TEST_SOURCE.text()).zoneId(zoneId).build(); + } + + protected static Configuration configurationForLocale(Locale locale) { + return randomConfigurationBuilder().query(TEST_SOURCE.text()).locale(locale).build(); + } + + protected static Configuration configurationForTimezoneAndLocale(ZoneId zoneId, Locale locale) { + return randomConfigurationBuilder().query(TEST_SOURCE.text()).zoneId(zoneId).locale(locale).build(); } } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractTests.java index fb7ec0ddb3897..26e427a76fc95 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateExtractTests.java @@ -11,6 +11,7 @@ import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import org.apache.lucene.util.BytesRef; +import org.elasticsearch.common.time.DateUtils; import org.elasticsearch.compute.operator.DriverContext; import org.elasticsearch.xpack.esql.EsqlTestUtils; import org.elasticsearch.xpack.esql.core.InvalidArgumentException; @@ -24,12 +25,15 @@ import org.elasticsearch.xpack.esql.session.Configuration; import java.time.Instant; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.ZonedDateTime; import java.time.temporal.ChronoField; import java.util.ArrayList; import java.util.List; import java.util.function.Supplier; +import static org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier.TEST_SOURCE; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.nullValue; @@ -46,30 +50,6 @@ public static Iterable parameters() { for (var stringType : DataType.stringTypes()) { suppliers.addAll( List.of( - new TestCaseSupplier( - List.of(stringType, DataType.DATETIME), - () -> new TestCaseSupplier.TestCase( - List.of( - new TestCaseSupplier.TypedData(new BytesRef("YeAr"), stringType, "chrono"), - new TestCaseSupplier.TypedData(1687944333000L, DataType.DATETIME, "date") - ), - "DateExtractMillisEvaluator[value=Attribute[channel=1], chronoField=Attribute[channel=0], zone=Z]", - DataType.LONG, - equalTo(2023L) - ).withStaticConfiguration() - ), - new TestCaseSupplier( - List.of(stringType, DataType.DATE_NANOS), - () -> new TestCaseSupplier.TestCase( - List.of( - new TestCaseSupplier.TypedData(new BytesRef("YeAr"), stringType, "chrono"), - new TestCaseSupplier.TypedData(1687944333000000000L, DataType.DATE_NANOS, "date") - ), - "DateExtractNanosEvaluator[value=Attribute[channel=1], chronoField=Attribute[channel=0], zone=Z]", - DataType.LONG, - equalTo(2023L) - ).withStaticConfiguration() - ), new TestCaseSupplier( List.of(stringType, DataType.DATE_NANOS), () -> new TestCaseSupplier.TestCase( @@ -80,7 +60,7 @@ public static Iterable parameters() { "DateExtractNanosEvaluator[value=Attribute[channel=1], chronoField=Attribute[channel=0], zone=Z]", DataType.LONG, equalTo(123456L) - ).withStaticConfiguration() + ).withConfiguration(TestCaseSupplier.TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC)) ), new TestCaseSupplier( List.of(stringType, DataType.DATETIME), @@ -93,7 +73,7 @@ public static Iterable parameters() { "DateExtractMillisEvaluator[value=Attribute[channel=1], chronoField=Attribute[channel=0], zone=Z]", DataType.LONG, is(nullValue()) - ).withStaticConfiguration() + ).withConfiguration(TestCaseSupplier.TEST_SOURCE, configurationForTimezone(ZoneOffset.UTC)) .withWarning( "Line 1:1: evaluation of [source] failed, treating result as null. Only first 20 failures recorded." ) @@ -107,9 +87,46 @@ public static Iterable parameters() { ); } + suppliers.addAll(casesFor("YeAr", "2023-11-04T16:13:12Z", "Z", 2023)); + suppliers.addAll(casesFor("day_of_month", "2020-01-01T00:00:00Z", "America/New_York", 31)); + suppliers.addAll(casesFor("month_of_year", "2020-06-30T23:00:00Z", "Europe/Paris", 7)); + return parameterSuppliersFromTypedDataWithDefaultChecks(true, suppliers); } + private static List casesFor(String field, String date, String zoneIdName, long expectedResult) { + long dateMillis = Instant.parse(date).toEpochMilli(); + ZoneId zoneId = ZoneId.of(zoneIdName); + return List.of( + new TestCaseSupplier( + field + " - " + date + " (millis) - " + zoneId, + List.of(DataType.KEYWORD, DataType.DATETIME), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(field, DataType.KEYWORD, "field"), + new TestCaseSupplier.TypedData(dateMillis, DataType.DATETIME, "date") + ), + "DateExtractMillisEvaluator[value=Attribute[channel=1], chronoField=Attribute[channel=0], zone=" + zoneId + "]", + DataType.LONG, + equalTo(expectedResult) + ).withConfiguration(TEST_SOURCE, configurationForTimezone(zoneId)) + ), + new TestCaseSupplier( + field + " - " + date + " (nanos) - " + zoneId, + List.of(DataType.KEYWORD, DataType.DATE_NANOS), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(field, DataType.KEYWORD, "field"), + new TestCaseSupplier.TypedData(DateUtils.toNanoSeconds(dateMillis), DataType.DATE_NANOS, "date") + ), + "DateExtractNanosEvaluator[value=Attribute[channel=1], chronoField=Attribute[channel=0], zone=" + zoneId + "]", + DataType.LONG, + equalTo(expectedResult) + ).withConfiguration(TEST_SOURCE, configurationForTimezone(zoneId)) + ) + ); + } + public void testAllChronoFields() { long epochMilli = 1687944333123L; ZonedDateTime date = Instant.ofEpochMilli(epochMilli).atZone(EsqlTestUtils.TEST_CFG.zoneId()); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatTests.java index 10ead11f09273..b291c2192538f 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DateFormatTests.java @@ -24,8 +24,11 @@ import java.time.Instant; import java.util.ArrayList; import java.util.List; +import java.util.Locale; import java.util.function.Supplier; +import static org.elasticsearch.test.ReadableMatchers.matchesBytesRef; +import static org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier.TEST_SOURCE; import static org.hamcrest.Matchers.matchesPattern; public class DateFormatTests extends AbstractConfigurationFunctionTestCase { @@ -84,10 +87,56 @@ public static Iterable parameters() { (value) -> new BytesRef(EsqlDataTypeConverter.DEFAULT_DATE_TIME_FORMATTER.formatNanos(DateUtils.toLong((Instant) value))), List.of() ); - suppliers = TestCaseSupplier.mapTestCases(suppliers, testCase -> testCase.withStaticConfiguration()); + suppliers = TestCaseSupplier.mapTestCases( + suppliers, + testCase -> testCase.withConfiguration(TestCaseSupplier.TEST_SOURCE, configurationForLocale(Locale.US)) + ); + suppliers.addAll(casesFor("MMM", "2020-01-01T00:00:00.00Z", "es-es", "ene")); + suppliers.addAll(casesFor("VV", "2020-01-01T00:00:00.00Z", "es-es", "Z")); return parameterSuppliersFromTypedDataWithDefaultChecks(true, suppliers); } + private static List casesFor(String format, String date, String localeTag, String expectedString) { + long dateMillis = Instant.parse(date).toEpochMilli(); + Locale locale = Locale.forLanguageTag(localeTag); + return List.of( + new TestCaseSupplier( + format + " - " + date + " (millis) - " + locale, + List.of(DataType.KEYWORD, DataType.DATETIME), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(format, DataType.KEYWORD, "format"), + new TestCaseSupplier.TypedData(dateMillis, DataType.DATETIME, "date") + ), + matchesPattern( + "DateFormatMillisEvaluator\\[val=Attribute\\[channel=1], formatter=Attribute\\[(channel=0|\\w+)], locale=" + + locale + + "]" + ), + DataType.KEYWORD, + matchesBytesRef(expectedString) + ).withConfiguration(TEST_SOURCE, configurationForLocale(locale)) + ), + new TestCaseSupplier( + format + " - " + date + " (nanos) - " + locale, + List.of(DataType.KEYWORD, DataType.DATE_NANOS), + () -> new TestCaseSupplier.TestCase( + List.of( + new TestCaseSupplier.TypedData(format, DataType.KEYWORD, "format"), + new TestCaseSupplier.TypedData(DateUtils.toNanoSeconds(dateMillis), DataType.DATE_NANOS, "date") + ), + matchesPattern( + "DateFormatNanosEvaluator\\[val=Attribute\\[channel=1], formatter=Attribute\\[(channel=0|\\w+)], locale=" + + locale + + "]" + ), + DataType.KEYWORD, + matchesBytesRef(expectedString) + ).withConfiguration(TEST_SOURCE, configurationForLocale(locale)) + ) + ); + } + @Override protected Expression buildWithConfiguration(Source source, List args, Configuration configuration) { return new DateFormat(source, args.get(0), args.size() == 2 ? args.get(1) : null, configuration); diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DayNameTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DayNameTests.java index b50377660c8c5..4fe528a12216c 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DayNameTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/DayNameTests.java @@ -10,7 +10,6 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; -import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.lucene.BytesRefs; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.time.DateUtils; @@ -35,6 +34,7 @@ import java.util.Map; import java.util.function.Supplier; +import static org.elasticsearch.test.ReadableMatchers.matchesBytesRef; import static org.hamcrest.Matchers.equalTo; public class DayNameTests extends AbstractConfigurationFunctionTestCase { @@ -46,48 +46,61 @@ public DayNameTests(@Name("TestCase") Supplier testCa @ParametersFactory public static Iterable parameters() { List suppliers = new ArrayList<>(); - suppliers.addAll(generateTest("2019-03-11T00:00:00.00Z", "Monday")); - suppliers.addAll(generateTest("2022-07-26T23:59:59.99Z", "Tuesday")); - suppliers.addAll(generateTest("2017-10-11T23:12:32.12Z", "Wednesday")); - suppliers.addAll(generateTest("2023-01-05T07:39:01.28Z", "Thursday")); - suppliers.addAll(generateTest("2023-02-17T10:25:33.38Z", "Friday")); - suppliers.addAll(generateTest("2013-06-15T22:55:33.82Z", "Saturday")); - suppliers.addAll(generateTest("2024-08-18T01:01:29.49Z", "Sunday")); + + // UTC, English + suppliers.addAll(generateTest("2019-03-11T00:00:00.00Z", "Monday", "Z", "en")); + suppliers.addAll(generateTest("2022-07-26T23:59:59.99Z", "Tuesday", "Z", "en")); + suppliers.addAll(generateTest("2017-10-11T23:12:32.12Z", "Wednesday", "Z", "en")); + suppliers.addAll(generateTest("2023-01-05T07:39:01.28Z", "Thursday", "Z", "en")); + suppliers.addAll(generateTest("2023-02-17T10:25:33.38Z", "Friday", "Z", "en")); + suppliers.addAll(generateTest("2013-06-15T22:55:33.82Z", "Saturday", "Z", "en")); + suppliers.addAll(generateTest("2024-08-18T01:01:29.49Z", "Sunday", "Z", "en")); + + // Other timezones and locales + suppliers.addAll(generateTest("2019-03-11T22:00:00.00Z", "Tuesday", "+05:00", "en")); + suppliers.addAll(generateTest("2019-03-11T00:00:00.00Z", "Sunday", "America/New_York", "en")); + suppliers.addAll(generateTest("2019-03-11T00:00:00.00Z", "lunes", "Z", "es")); + suppliers.addAll(generateTest("2019-03-11T00:00:00.00Z", "domingo", "America/New_York", "es")); suppliers.add( new TestCaseSupplier( + "Null", List.of(DataType.DATETIME), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(null, DataType.DATETIME, "date")), - Matchers.startsWith("DayNameMillisEvaluator[val=Attribute[channel=0], zoneId=Z, locale=en_US]"), + Matchers.startsWith("DayNameMillisEvaluator[val=Attribute[channel=0], zoneId="), DataType.KEYWORD, equalTo(null) - ).withStaticConfiguration() + ) ) ); return parameterSuppliersFromTypedDataWithDefaultChecks(true, suppliers); } - private static List generateTest(String dateTime, String expectedWeekDay) { + private static List generateTest(String dateTime, String expectedWeekDay, String zoneIdString, String localeTag) { + ZoneId zoneId = ZoneId.of(zoneIdString); + Locale locale = Locale.forLanguageTag(localeTag); return List.of( new TestCaseSupplier( + dateTime + " (millis) - " + zoneId + ", " + locale, List.of(DataType.DATETIME), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(toMillis(dateTime), DataType.DATETIME, "date")), - Matchers.startsWith("DayNameMillisEvaluator[val=Attribute[channel=0], zoneId=Z, locale=en_US]"), + Matchers.startsWith("DayNameMillisEvaluator[val=Attribute[channel=0], zoneId=" + zoneId + ", locale=" + locale + "]"), DataType.KEYWORD, - equalTo(new BytesRef(expectedWeekDay)) - ).withStaticConfiguration() + matchesBytesRef(expectedWeekDay) + ).withConfiguration(TestCaseSupplier.TEST_SOURCE, configurationForTimezoneAndLocale(zoneId, locale)) ), new TestCaseSupplier( + dateTime + " (nanos) - " + zoneId + ", " + locale, List.of(DataType.DATE_NANOS), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(toNanos(dateTime), DataType.DATE_NANOS, "date")), - Matchers.is("DayNameNanosEvaluator[val=Attribute[channel=0], zoneId=Z, locale=en_US]"), + Matchers.is("DayNameNanosEvaluator[val=Attribute[channel=0], zoneId=" + zoneId + ", locale=" + locale + "]"), DataType.KEYWORD, - equalTo(new BytesRef(expectedWeekDay)) - ).withStaticConfiguration() + matchesBytesRef(expectedWeekDay) + ).withConfiguration(TestCaseSupplier.TEST_SOURCE, configurationForTimezoneAndLocale(zoneId, locale)) ) ); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/MonthNameTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/MonthNameTests.java index 9c4e6c2007b12..698d36232f665 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/MonthNameTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/MonthNameTests.java @@ -10,7 +10,6 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; -import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.lucene.BytesRefs; import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.time.DateUtils; @@ -35,6 +34,7 @@ import java.util.Map; import java.util.function.Supplier; +import static org.elasticsearch.test.ReadableMatchers.matchesBytesRef; import static org.hamcrest.Matchers.equalTo; public class MonthNameTests extends AbstractConfigurationFunctionTestCase { @@ -51,53 +51,64 @@ protected Expression buildWithConfiguration(Source source, List args @ParametersFactory public static Iterable parameters() { List suppliers = new ArrayList<>(); - suppliers.addAll(generateTest("1994-01-19T00:00:00.00Z", "January")); - suppliers.addAll(generateTest("1995-02-20T23:59:59.99Z", "February")); - suppliers.addAll(generateTest("1996-03-21T23:12:32.12Z", "March")); - suppliers.addAll(generateTest("1997-04-22T07:39:01.28Z", "April")); - suppliers.addAll(generateTest("1998-05-23T10:25:33.38Z", "May")); - suppliers.addAll(generateTest("1999-06-24T22:55:33.82Z", "June")); - suppliers.addAll(generateTest("2000-07-25T01:01:29.49Z", "July")); - suppliers.addAll(generateTest("2001-08-25T01:01:29.49Z", "August")); - suppliers.addAll(generateTest("2002-09-25T01:01:29.49Z", "September")); - suppliers.addAll(generateTest("2003-10-25T01:01:29.49Z", "October")); - suppliers.addAll(generateTest("2004-11-25T01:01:29.49Z", "November")); - suppliers.addAll(generateTest("2005-12-25T01:01:29.49Z", "December")); + suppliers.addAll(generateTest("1994-01-19T00:00:00.00Z", "January", "Z", "en")); + suppliers.addAll(generateTest("1995-02-20T23:59:59.99Z", "February", "Z", "en")); + suppliers.addAll(generateTest("1996-03-21T23:12:32.12Z", "March", "Z", "en")); + suppliers.addAll(generateTest("1997-04-22T07:39:01.28Z", "April", "Z", "en")); + suppliers.addAll(generateTest("1998-05-23T10:25:33.38Z", "May", "Z", "en")); + suppliers.addAll(generateTest("1999-06-24T22:55:33.82Z", "June", "Z", "en")); + suppliers.addAll(generateTest("2000-07-25T01:01:29.49Z", "July", "Z", "en")); + suppliers.addAll(generateTest("2001-08-25T01:01:29.49Z", "August", "Z", "en")); + suppliers.addAll(generateTest("2002-09-25T01:01:29.49Z", "September", "Z", "en")); + suppliers.addAll(generateTest("2003-10-25T01:01:29.49Z", "October", "Z", "en")); + suppliers.addAll(generateTest("2004-11-25T01:01:29.49Z", "November", "Z", "en")); + suppliers.addAll(generateTest("2005-12-25T01:01:29.49Z", "December", "Z", "en")); + + // Other timezones and locales + suppliers.addAll(generateTest("2019-03-31T22:00:00.00Z", "April", "+05:00", "en")); + suppliers.addAll(generateTest("2019-03-01T00:00:00.00Z", "February", "America/New_York", "en")); + suppliers.addAll(generateTest("2019-03-11T00:00:00.00Z", "marzo", "Z", "es")); + suppliers.addAll(generateTest("2019-03-01T00:00:00.00Z", "febrero", "America/New_York", "es")); suppliers.add( new TestCaseSupplier( + "Null", List.of(DataType.DATETIME), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(null, DataType.DATETIME, "date")), - Matchers.startsWith("MonthNameMillisEvaluator[val=Attribute[channel=0], zoneId=Z, locale=en_US]"), + Matchers.startsWith("MonthNameMillisEvaluator[val=Attribute[channel=0], zoneId="), DataType.KEYWORD, equalTo(null) - ).withStaticConfiguration() + ) ) ); return parameterSuppliersFromTypedDataWithDefaultChecks(true, suppliers); } - private static List generateTest(String dateTime, String expectedMonthName) { + private static List generateTest(String dateTime, String expectedMonthName, String zoneIdString, String localeTag) { + ZoneId zoneId = ZoneId.of(zoneIdString); + Locale locale = Locale.forLanguageTag(localeTag); return List.of( new TestCaseSupplier( + expectedMonthName, List.of(DataType.DATETIME), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(toMillis(dateTime), DataType.DATETIME, "date")), - Matchers.startsWith("MonthNameMillisEvaluator[val=Attribute[channel=0], zoneId=Z, locale=en_US]"), + Matchers.startsWith("MonthNameMillisEvaluator[val=Attribute[channel=0], zoneId=" + zoneId + ", locale=" + locale + "]"), DataType.KEYWORD, - equalTo(new BytesRef(expectedMonthName)) - ).withStaticConfiguration() + matchesBytesRef(expectedMonthName) + ).withConfiguration(TestCaseSupplier.TEST_SOURCE, configurationForTimezoneAndLocale(zoneId, locale)) ), new TestCaseSupplier( + expectedMonthName, List.of(DataType.DATE_NANOS), () -> new TestCaseSupplier.TestCase( List.of(new TestCaseSupplier.TypedData(toNanos(dateTime), DataType.DATE_NANOS, "date")), - Matchers.is("MonthNameNanosEvaluator[val=Attribute[channel=0], zoneId=Z, locale=en_US]"), + Matchers.is("MonthNameNanosEvaluator[val=Attribute[channel=0], zoneId=" + zoneId + ", locale=" + locale + "]"), DataType.KEYWORD, - equalTo(new BytesRef(expectedMonthName)) - ).withStaticConfiguration() + matchesBytesRef(expectedMonthName) + ).withConfiguration(TestCaseSupplier.TEST_SOURCE, configurationForTimezoneAndLocale(zoneId, locale)) ) ); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/NowTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/NowTests.java index ef5d6c2a18950..2c3ed6e8cdf88 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/NowTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/date/NowTests.java @@ -10,6 +10,7 @@ import com.carrotsearch.randomizedtesting.annotations.Name; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; +import org.elasticsearch.xpack.esql.ConfigurationTestUtils; import org.elasticsearch.xpack.esql.core.expression.Expression; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; @@ -31,21 +32,15 @@ public NowTests(@Name("TestCase") Supplier testCaseSu @ParametersFactory public static Iterable parameters() { - return parameterSuppliersFromTypedDataWithDefaultChecks( - true, - List.of( - new TestCaseSupplier( - "Now Test", - List.of(), - () -> new TestCaseSupplier.TestCase( - List.of(), - matchesPattern("LiteralsEvaluator\\[lit=.*]"), - DataType.DATETIME, - equalTo(TestCaseSupplier.TEST_CONFIGURATION.now().toInstant().toEpochMilli()) - ).withStaticConfiguration() - ) - ) - ); + return parameterSuppliersFromTypedDataWithDefaultChecks(true, List.of(new TestCaseSupplier("Now Test", List.of(), () -> { + var configuration = ConfigurationTestUtils.randomConfigurationBuilder().query(TestCaseSupplier.TEST_SOURCE.text()).build(); + return new TestCaseSupplier.TestCase( + List.of(), + matchesPattern("LiteralsEvaluator\\[lit=.*]"), + DataType.DATETIME, + equalTo(configuration.now().toInstant().toEpochMilli()) + ).withConfiguration(TestCaseSupplier.TEST_SOURCE, configuration); + }))); } @Override diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLowerTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLowerTests.java index 894b1249426ec..ea05b7f75b5bc 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLowerTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToLowerTests.java @@ -12,25 +12,22 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.lucene.BytesRefs; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.xpack.esql.ConfigurationTestUtils; import org.elasticsearch.xpack.esql.EsqlTestUtils; -import org.elasticsearch.xpack.esql.analysis.AnalyzerSettings; import org.elasticsearch.xpack.esql.core.expression.Expression; import org.elasticsearch.xpack.esql.core.expression.FoldContext; import org.elasticsearch.xpack.esql.core.expression.Literal; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; -import org.elasticsearch.xpack.esql.core.util.DateUtils; import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; import org.elasticsearch.xpack.esql.expression.function.scalar.AbstractConfigurationFunctionTestCase; -import org.elasticsearch.xpack.esql.plugin.QueryPragmas; import org.elasticsearch.xpack.esql.session.Configuration; import java.util.ArrayList; import java.util.List; -import java.util.Map; +import java.util.Locale; import java.util.function.Supplier; import static org.hamcrest.Matchers.equalTo; @@ -53,30 +50,11 @@ public static Iterable parameters() { public void testRandomLocale() { String testString = randomAlphaOfLength(10); - Configuration cfg = randomLocaleConfig(); + Configuration cfg = ConfigurationTestUtils.randomConfiguration(); ToLower func = new ToLower(Source.EMPTY, Literal.keyword(Source.EMPTY, testString), cfg); assertThat(BytesRefs.toBytesRef(testString.toLowerCase(cfg.locale())), equalTo(func.fold(FoldContext.small()))); } - private Configuration randomLocaleConfig() { - return new Configuration( - DateUtils.UTC, - randomLocale(random()), - null, - null, - QueryPragmas.EMPTY, - AnalyzerSettings.QUERY_RESULT_TRUNCATION_MAX_SIZE.getDefault(Settings.EMPTY), - AnalyzerSettings.QUERY_RESULT_TRUNCATION_DEFAULT_SIZE.getDefault(Settings.EMPTY), - "", - false, - Map.of(), - System.nanoTime(), - randomBoolean(), - AnalyzerSettings.QUERY_TIMESERIES_RESULT_TRUNCATION_MAX_SIZE.getDefault(Settings.EMPTY), - AnalyzerSettings.QUERY_TIMESERIES_RESULT_TRUNCATION_DEFAULT_SIZE.getDefault(Settings.EMPTY) - ); - } - @Override protected Expression buildWithConfiguration(Source source, List args, Configuration configuration) { return new ToLower(source, args.get(0), configuration); @@ -91,8 +69,10 @@ private static void suppliers(List suppliers, String name, Dat values.add(new TestCaseSupplier.TypedData(new BytesRef(value), type, "0")); String expectedValue = value.toLowerCase(EsqlTestUtils.TEST_CFG.locale()); - return new TestCaseSupplier.TestCase(values, expectedToString, type, equalTo(new BytesRef(expectedValue))) - .withStaticConfiguration(); + return new TestCaseSupplier.TestCase(values, expectedToString, type, equalTo(new BytesRef(expectedValue))).withConfiguration( + TestCaseSupplier.TEST_SOURCE, + configurationForLocale(Locale.US) + ); })); suppliers.add(new TestCaseSupplier(name + " mv", List.of(type), () -> { List values = new ArrayList<>(); @@ -102,7 +82,10 @@ private static void suppliers(List suppliers, String name, Dat values.add(new TestCaseSupplier.TypedData(strings.stream().map(BytesRef::new).toList(), type, "0")); List expectedValue = strings.stream().map(s -> new BytesRef(s.toLowerCase(EsqlTestUtils.TEST_CFG.locale()))).toList(); - return new TestCaseSupplier.TestCase(values, expectedToString, type, equalTo(expectedValue)).withStaticConfiguration(); + return new TestCaseSupplier.TestCase(values, expectedToString, type, equalTo(expectedValue)).withConfiguration( + TestCaseSupplier.TEST_SOURCE, + configurationForLocale(Locale.US) + ); })); } diff --git a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpperTests.java b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpperTests.java index c3ab1ae0c2614..a4d8d5083dfbd 100644 --- a/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpperTests.java +++ b/x-pack/plugin/esql/src/test/java/org/elasticsearch/xpack/esql/expression/function/scalar/string/ToUpperTests.java @@ -12,25 +12,22 @@ import org.apache.lucene.util.BytesRef; import org.elasticsearch.common.lucene.BytesRefs; -import org.elasticsearch.common.settings.Settings; import org.elasticsearch.compute.data.Block; import org.elasticsearch.compute.data.Page; +import org.elasticsearch.xpack.esql.ConfigurationTestUtils; import org.elasticsearch.xpack.esql.EsqlTestUtils; -import org.elasticsearch.xpack.esql.analysis.AnalyzerSettings; import org.elasticsearch.xpack.esql.core.expression.Expression; import org.elasticsearch.xpack.esql.core.expression.FoldContext; import org.elasticsearch.xpack.esql.core.expression.Literal; import org.elasticsearch.xpack.esql.core.tree.Source; import org.elasticsearch.xpack.esql.core.type.DataType; -import org.elasticsearch.xpack.esql.core.util.DateUtils; import org.elasticsearch.xpack.esql.expression.function.TestCaseSupplier; import org.elasticsearch.xpack.esql.expression.function.scalar.AbstractConfigurationFunctionTestCase; -import org.elasticsearch.xpack.esql.plugin.QueryPragmas; import org.elasticsearch.xpack.esql.session.Configuration; import java.util.ArrayList; import java.util.List; -import java.util.Map; +import java.util.Locale; import java.util.function.Supplier; import static org.hamcrest.Matchers.equalTo; @@ -53,30 +50,11 @@ public static Iterable parameters() { public void testRandomLocale() { String testString = randomAlphaOfLength(10); - Configuration cfg = randomLocaleConfig(); + Configuration cfg = ConfigurationTestUtils.randomConfiguration(); ToUpper func = new ToUpper(Source.EMPTY, Literal.keyword(Source.EMPTY, testString), cfg); assertThat(BytesRefs.toBytesRef(testString.toUpperCase(cfg.locale())), equalTo(func.fold(FoldContext.small()))); } - private Configuration randomLocaleConfig() { - return new Configuration( - DateUtils.UTC, - randomLocale(random()), - null, - null, - QueryPragmas.EMPTY, - AnalyzerSettings.QUERY_RESULT_TRUNCATION_MAX_SIZE.getDefault(Settings.EMPTY), - AnalyzerSettings.QUERY_RESULT_TRUNCATION_DEFAULT_SIZE.getDefault(Settings.EMPTY), - "", - false, - Map.of(), - System.nanoTime(), - randomBoolean(), - AnalyzerSettings.QUERY_TIMESERIES_RESULT_TRUNCATION_MAX_SIZE.getDefault(Settings.EMPTY), - AnalyzerSettings.QUERY_TIMESERIES_RESULT_TRUNCATION_DEFAULT_SIZE.getDefault(Settings.EMPTY) - ); - } - @Override protected Expression buildWithConfiguration(Source source, List args, Configuration configuration) { return new ToUpper(source, args.get(0), configuration); @@ -91,8 +69,10 @@ private static void supplier(List suppliers, String name, Data values.add(new TestCaseSupplier.TypedData(new BytesRef(value), type, "0")); String expectedValue = value.toUpperCase(EsqlTestUtils.TEST_CFG.locale()); - return new TestCaseSupplier.TestCase(values, expectedToString, type, equalTo(new BytesRef(expectedValue))) - .withStaticConfiguration(); + return new TestCaseSupplier.TestCase(values, expectedToString, type, equalTo(new BytesRef(expectedValue))).withConfiguration( + TestCaseSupplier.TEST_SOURCE, + configurationForLocale(Locale.US) + ); })); suppliers.add(new TestCaseSupplier(name + " mv", List.of(type), () -> { List values = new ArrayList<>(); @@ -102,7 +82,10 @@ private static void supplier(List suppliers, String name, Data values.add(new TestCaseSupplier.TypedData(strings.stream().map(BytesRef::new).toList(), type, "0")); List expectedValue = strings.stream().map(s -> new BytesRef(s.toUpperCase(EsqlTestUtils.TEST_CFG.locale()))).toList(); - return new TestCaseSupplier.TestCase(values, expectedToString, type, equalTo(expectedValue)).withStaticConfiguration(); + return new TestCaseSupplier.TestCase(values, expectedToString, type, equalTo(expectedValue)).withConfiguration( + TestCaseSupplier.TEST_SOURCE, + configurationForLocale(Locale.US) + ); })); }