-
Notifications
You must be signed in to change notification settings - Fork 25.9k
[ESQL] Remove Named Expcted Types map from testing infrastructure #111213
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
elasticsearchmachine
merged 7 commits into
elastic:main
from
not-napoleon:esql-remove-named-expected-types
Jul 24, 2024
Merged
Changes from all commits
Commits
Show all changes
7 commits
Select commit
Hold shift + click to select a range
b43a286
wire up new first layer
not-napoleon 043e993
wire up a bunch of explicit type strings
not-napoleon 1c132c4
binary comparision tests
not-napoleon 6792e6b
of course concat is special
not-napoleon 026ad66
remove that blasted map
not-napoleon bb8fbce
spotless apply
not-napoleon fedab70
added some javadoc
not-napoleon File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,12 +34,9 @@ | |
| import org.hamcrest.Matcher; | ||
|
|
||
| import java.util.ArrayList; | ||
| import java.util.Arrays; | ||
| import java.util.Comparator; | ||
| import java.util.HashSet; | ||
| import java.util.List; | ||
| import java.util.Locale; | ||
| import java.util.Map; | ||
| import java.util.Set; | ||
| import java.util.concurrent.ExecutionException; | ||
| import java.util.concurrent.ExecutorService; | ||
|
|
@@ -74,10 +71,14 @@ public abstract class AbstractScalarFunctionTestCase extends AbstractFunctionTes | |
| */ | ||
| protected static Iterable<Object[]> parameterSuppliersFromTypedDataWithDefaultChecks( | ||
| boolean entirelyNullPreservesType, | ||
| List<TestCaseSupplier> suppliers | ||
| List<TestCaseSupplier> suppliers, | ||
| PositionalErrorMessageSupplier positionalErrorMessageSupplier | ||
| ) { | ||
| return parameterSuppliersFromTypedData( | ||
| errorsForCasesWithoutExamples(anyNullIsNull(entirelyNullPreservesType, randomizeBytesRefsOffset(suppliers))) | ||
| errorsForCasesWithoutExamples( | ||
| anyNullIsNull(entirelyNullPreservesType, randomizeBytesRefsOffset(suppliers)), | ||
| positionalErrorMessageSupplier | ||
| ) | ||
| ); | ||
| } | ||
|
|
||
|
|
@@ -480,8 +481,14 @@ protected static List<TestCaseSupplier> anyNullIsNull( | |
| * Adds test cases containing unsupported parameter types that assert | ||
| * that they throw type errors. | ||
| */ | ||
| protected static List<TestCaseSupplier> errorsForCasesWithoutExamples(List<TestCaseSupplier> testCaseSuppliers) { | ||
| return errorsForCasesWithoutExamples(testCaseSuppliers, AbstractScalarFunctionTestCase::typeErrorMessage); | ||
| protected static List<TestCaseSupplier> errorsForCasesWithoutExamples( | ||
| List<TestCaseSupplier> testCaseSuppliers, | ||
| PositionalErrorMessageSupplier positionalErrorMessageSupplier | ||
| ) { | ||
| return errorsForCasesWithoutExamples( | ||
| testCaseSuppliers, | ||
| (i, v, t) -> AbstractScalarFunctionTestCase.typeErrorMessage(i, v, t, positionalErrorMessageSupplier) | ||
| ); | ||
| } | ||
|
|
||
| protected static List<TestCaseSupplier> errorsForCasesWithoutExamples( | ||
|
|
@@ -515,10 +522,11 @@ protected static List<TestCaseSupplier> errorsForCasesWithoutExamples( | |
| public static String errorMessageStringForBinaryOperators( | ||
| boolean includeOrdinal, | ||
| List<Set<DataType>> validPerPosition, | ||
| List<DataType> types | ||
| List<DataType> types, | ||
| PositionalErrorMessageSupplier positionalErrorMessageSupplier | ||
| ) { | ||
| try { | ||
| return typeErrorMessage(includeOrdinal, validPerPosition, types); | ||
| return typeErrorMessage(includeOrdinal, validPerPosition, types, positionalErrorMessageSupplier); | ||
| } catch (IllegalStateException e) { | ||
| // This means all the positional args were okay, so the expected error is from the combination | ||
| if (types.get(0).equals(DataType.UNSIGNED_LONG)) { | ||
|
|
@@ -617,12 +625,33 @@ protected interface TypeErrorMessageSupplier { | |
| String apply(boolean includeOrdinal, List<Set<DataType>> validPerPosition, List<DataType> types); | ||
| } | ||
|
|
||
| @FunctionalInterface | ||
| protected interface PositionalErrorMessageSupplier { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably wants javadoc given the number of places we're using it. |
||
| /** | ||
| * This interface defines functions to supply error messages for incorrect types in specific positions. Functions which have | ||
| * the same type requirements for all positions can simplify this with a lambda returning a string constant. | ||
| * | ||
| * @param validForPosition - the set of {@link DataType}s that the test infrastructure believes to be allowable in the | ||
| * given position. | ||
| * @param position - the zero-index position in the list of parameters the function has detected the bad argument to be. | ||
| * @return The string describing the acceptable parameters for that position. Note that this function should not return | ||
| * the full error string; that will be constructed by the test. Just return the type string for that position. | ||
| */ | ||
| String apply(Set<DataType> validForPosition, int position); | ||
| } | ||
|
|
||
| protected static TestCaseSupplier typeErrorSupplier( | ||
| boolean includeOrdinal, | ||
| List<Set<DataType>> validPerPosition, | ||
| List<DataType> types | ||
| List<DataType> types, | ||
| PositionalErrorMessageSupplier errorMessageSupplier | ||
| ) { | ||
| return typeErrorSupplier(includeOrdinal, validPerPosition, types, AbstractScalarFunctionTestCase::typeErrorMessage); | ||
| return typeErrorSupplier( | ||
| includeOrdinal, | ||
| validPerPosition, | ||
| types, | ||
| (o, v, t) -> AbstractScalarFunctionTestCase.typeErrorMessage(o, v, t, errorMessageSupplier) | ||
| ); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -647,7 +676,12 @@ protected static TestCaseSupplier typeErrorSupplier( | |
| /** | ||
| * Build the expected error message for an invalid type signature. | ||
| */ | ||
| protected static String typeErrorMessage(boolean includeOrdinal, List<Set<DataType>> validPerPosition, List<DataType> types) { | ||
| protected static String typeErrorMessage( | ||
| boolean includeOrdinal, | ||
| List<Set<DataType>> validPerPosition, | ||
| List<DataType> types, | ||
| PositionalErrorMessageSupplier expectedTypeSupplier | ||
| ) { | ||
| int badArgPosition = -1; | ||
| for (int i = 0; i < types.size(); i++) { | ||
| if (validPerPosition.get(i).contains(types.get(i)) == false) { | ||
|
|
@@ -661,209 +695,9 @@ protected static String typeErrorMessage(boolean includeOrdinal, List<Set<DataTy | |
| ); | ||
| } | ||
| String ordinal = includeOrdinal ? TypeResolutions.ParamOrdinal.fromIndex(badArgPosition).name().toLowerCase(Locale.ROOT) + " " : ""; | ||
| String expectedType = expectedType(validPerPosition.get(badArgPosition)); | ||
| String expectedTypeString = expectedTypeSupplier.apply(validPerPosition.get(badArgPosition), badArgPosition); | ||
| String name = types.get(badArgPosition).typeName(); | ||
| return ordinal + "argument of [] must be [" + expectedType + "], found value [" + name + "] type [" + name + "]"; | ||
| } | ||
|
|
||
| private static final Map<Set<DataType>, String> NAMED_EXPECTED_TYPES = Map.ofEntries( | ||
| Map.entry( | ||
| Set.of(DataType.DATE_PERIOD, DataType.DOUBLE, DataType.INTEGER, DataType.LONG, DataType.TIME_DURATION, DataType.NULL), | ||
| "numeric, date_period or time_duration" | ||
| ), | ||
| Map.entry(Set.of(DataType.DATETIME, DataType.NULL), "datetime"), | ||
| Map.entry(Set.of(DataType.DOUBLE, DataType.NULL), "double"), | ||
| Map.entry(Set.of(DataType.INTEGER, DataType.NULL), "integer"), | ||
| Map.entry(Set.of(DataType.IP, DataType.NULL), "ip"), | ||
| Map.entry(Set.of(DataType.LONG, DataType.INTEGER, DataType.UNSIGNED_LONG, DataType.DOUBLE, DataType.NULL), "numeric"), | ||
| Map.entry(Set.of(DataType.LONG, DataType.INTEGER, DataType.UNSIGNED_LONG, DataType.DOUBLE), "numeric"), | ||
| Map.entry(Set.of(DataType.KEYWORD, DataType.TEXT, DataType.VERSION, DataType.NULL), "string or version"), | ||
| Map.entry(Set.of(DataType.KEYWORD, DataType.TEXT, DataType.NULL), "string"), | ||
| Map.entry(Set.of(DataType.IP, DataType.KEYWORD, DataType.TEXT, DataType.NULL), "ip or string"), | ||
| Map.entry(Set.copyOf(Arrays.asList(representableTypes())), "representable"), | ||
| Map.entry(Set.copyOf(Arrays.asList(representableNonSpatialTypes())), "representableNonSpatial"), | ||
| Map.entry( | ||
| Set.of( | ||
| DataType.BOOLEAN, | ||
| DataType.DOUBLE, | ||
| DataType.INTEGER, | ||
| DataType.KEYWORD, | ||
| DataType.LONG, | ||
| DataType.TEXT, | ||
| DataType.UNSIGNED_LONG, | ||
| DataType.NULL | ||
| ), | ||
| "boolean or numeric or string" | ||
| ), | ||
| Map.entry( | ||
| Set.of( | ||
| DataType.DATETIME, | ||
| DataType.DOUBLE, | ||
| DataType.INTEGER, | ||
| DataType.KEYWORD, | ||
| DataType.LONG, | ||
| DataType.TEXT, | ||
| DataType.UNSIGNED_LONG, | ||
| DataType.NULL | ||
| ), | ||
| "datetime or numeric or string" | ||
| ), | ||
| // What Add accepts | ||
| Map.entry( | ||
| Set.of( | ||
| DataType.DATE_PERIOD, | ||
| DataType.DATETIME, | ||
| DataType.DOUBLE, | ||
| DataType.INTEGER, | ||
| DataType.LONG, | ||
| DataType.NULL, | ||
| DataType.TIME_DURATION, | ||
| DataType.UNSIGNED_LONG | ||
| ), | ||
| "datetime or numeric" | ||
| ), | ||
| Map.entry( | ||
| Set.of( | ||
| DataType.BOOLEAN, | ||
| DataType.DATETIME, | ||
| DataType.DOUBLE, | ||
| DataType.INTEGER, | ||
| DataType.KEYWORD, | ||
| DataType.LONG, | ||
| DataType.TEXT, | ||
| DataType.UNSIGNED_LONG, | ||
| DataType.NULL | ||
| ), | ||
| "boolean or datetime or numeric or string" | ||
| ), | ||
| // to_int | ||
| Map.entry( | ||
| Set.of( | ||
| DataType.BOOLEAN, | ||
| DataType.COUNTER_INTEGER, | ||
| DataType.DATETIME, | ||
| DataType.DOUBLE, | ||
| DataType.INTEGER, | ||
| DataType.KEYWORD, | ||
| DataType.LONG, | ||
| DataType.TEXT, | ||
| DataType.UNSIGNED_LONG, | ||
| DataType.NULL | ||
| ), | ||
| "boolean or counter_integer or datetime or numeric or string" | ||
| ), | ||
| // to_long | ||
| Map.entry( | ||
| Set.of( | ||
| DataType.BOOLEAN, | ||
| DataType.COUNTER_INTEGER, | ||
| DataType.COUNTER_LONG, | ||
| DataType.DATETIME, | ||
| DataType.DOUBLE, | ||
| DataType.INTEGER, | ||
| DataType.KEYWORD, | ||
| DataType.LONG, | ||
| DataType.TEXT, | ||
| DataType.UNSIGNED_LONG, | ||
| DataType.NULL | ||
| ), | ||
| "boolean or counter_integer or counter_long or datetime or numeric or string" | ||
| ), | ||
| // to_double | ||
| Map.entry( | ||
| Set.of( | ||
| DataType.BOOLEAN, | ||
| DataType.COUNTER_DOUBLE, | ||
| DataType.COUNTER_INTEGER, | ||
| DataType.COUNTER_LONG, | ||
| DataType.DATETIME, | ||
| DataType.DOUBLE, | ||
| DataType.INTEGER, | ||
| DataType.KEYWORD, | ||
| DataType.LONG, | ||
| DataType.TEXT, | ||
| DataType.UNSIGNED_LONG, | ||
| DataType.NULL | ||
| ), | ||
| "boolean or counter_double or counter_integer or counter_long or datetime or numeric or string" | ||
| ), | ||
| Map.entry( | ||
| Set.of( | ||
| DataType.BOOLEAN, | ||
| DataType.CARTESIAN_POINT, | ||
| DataType.DATETIME, | ||
| DataType.DOUBLE, | ||
| DataType.GEO_POINT, | ||
| DataType.INTEGER, | ||
| DataType.KEYWORD, | ||
| DataType.LONG, | ||
| DataType.TEXT, | ||
| DataType.UNSIGNED_LONG, | ||
| DataType.NULL | ||
| ), | ||
| "boolean or cartesian_point or datetime or geo_point or numeric or string" | ||
| ), | ||
| Map.entry( | ||
| Set.of( | ||
| DataType.DATETIME, | ||
| DataType.DOUBLE, | ||
| DataType.INTEGER, | ||
| DataType.IP, | ||
| DataType.KEYWORD, | ||
| DataType.LONG, | ||
| DataType.TEXT, | ||
| DataType.UNSIGNED_LONG, | ||
| DataType.VERSION, | ||
| DataType.NULL | ||
| ), | ||
| "datetime, double, integer, ip, keyword, long, text, unsigned_long or version" | ||
| ), | ||
| Map.entry( | ||
| Set.of( | ||
| DataType.BOOLEAN, | ||
| DataType.DATETIME, | ||
| DataType.DOUBLE, | ||
| DataType.GEO_POINT, | ||
| DataType.GEO_SHAPE, | ||
| DataType.INTEGER, | ||
| DataType.IP, | ||
| DataType.KEYWORD, | ||
| DataType.LONG, | ||
| DataType.TEXT, | ||
| DataType.UNSIGNED_LONG, | ||
| DataType.VERSION, | ||
| DataType.NULL | ||
| ), | ||
| "cartesian_point or datetime or geo_point or numeric or string" | ||
| ), | ||
| Map.entry(Set.of(DataType.GEO_POINT, DataType.KEYWORD, DataType.TEXT, DataType.NULL), "geo_point or string"), | ||
| Map.entry(Set.of(DataType.CARTESIAN_POINT, DataType.KEYWORD, DataType.TEXT, DataType.NULL), "cartesian_point or string"), | ||
| Map.entry( | ||
| Set.of(DataType.GEO_POINT, DataType.GEO_SHAPE, DataType.KEYWORD, DataType.TEXT, DataType.NULL), | ||
| "geo_point or geo_shape or string" | ||
| ), | ||
| Map.entry( | ||
| Set.of(DataType.CARTESIAN_POINT, DataType.CARTESIAN_SHAPE, DataType.KEYWORD, DataType.TEXT, DataType.NULL), | ||
| "cartesian_point or cartesian_shape or string" | ||
| ), | ||
| Map.entry(Set.of(DataType.GEO_POINT, DataType.CARTESIAN_POINT, DataType.NULL), "geo_point or cartesian_point"), | ||
| Map.entry(Set.of(DataType.DATE_PERIOD, DataType.TIME_DURATION, DataType.NULL), "dateperiod or timeduration") | ||
| ); | ||
|
|
||
| // TODO: generate this message dynamically, a la AbstractConvertFunction#supportedTypesNames()? | ||
| private static String expectedType(Set<DataType> validTypes) { | ||
| String named = NAMED_EXPECTED_TYPES.get(validTypes); | ||
| if (named == null) { | ||
| /* | ||
| * Note for anyone who's test lands here - it's likely that you | ||
| * don't have a test case covering explicit `null` arguments in | ||
| * this position. Generally you can get that with anyNullIsNull. | ||
| */ | ||
| throw new UnsupportedOperationException( | ||
| "can't guess expected types for " + validTypes.stream().sorted(Comparator.comparing(t -> t.typeName())).toList() | ||
| ); | ||
| } | ||
| return named; | ||
| return ordinal + "argument of [] must be [" + expectedTypeString + "], found value [" + name + "] type [" + name + "]"; | ||
| } | ||
|
|
||
| protected static Stream<DataType> representable() { | ||
|
|
||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 for giving this a name.