Skip to content

Commit 91c700b

Browse files
authored
Super randomized tests for fetch fields API (#70278)
We've had a few bugs in the fields API where is doesn't behave like we'd expect. Typically this happens because it isn't obvious what we expct. So we'll try and use randomized testing to ferret out what we want. This adds a test for most field types that asserts that `fields` works similarly to `docvalues_fields`. We expect this to be true for most fields. It does so by forcing all subclasses of `MapperTestCase` to define a method that makes random values. It declares a few other hooks that subclasses can override to further randomize the test. We skip the test for a few field types that don't have doc values: * `annotated_text` * `completion` * `search_as_you_type` * `text` We should come up with some way to test these without doc values, even if it isn't as nice. But that is a problem for another time, I think. We skip the test for a few more types just because I wanted to cut this PR in half so we could get to reviewing it earlier. We'll get to those in a follow up change. I've filed a few bugs for things that are inconsistent with `docvalues_fields`. Typically that means that we have to limit the random values that we generate to those that *do* round trip properly.
1 parent afde502 commit 91c700b

File tree

42 files changed

+544
-36
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+544
-36
lines changed

modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/RankFeatureFieldMapperTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,10 @@ public void testRejectMultiValuedFields() throws MapperParsingException, IOExcep
136136
assertEquals("[rank_feature] fields do not support indexing multiple values for the same field [foo.field] in the same document",
137137
e.getCause().getMessage());
138138
}
139+
140+
@Override
141+
protected Object generateRandomInputValue(MappedFieldType ft) {
142+
assumeFalse("Test implemented in a follow up", true);
143+
return null;
144+
}
139145
}

modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/RankFeaturesFieldMapperTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,10 @@ public void testRejectMultiValuedFields() throws MapperParsingException, IOExcep
135135
assertEquals("[rank_features] fields do not support indexing multiple values for the same rank feature [foo.field.bar] in " +
136136
"the same document", e.getCause().getMessage());
137137
}
138+
139+
@Override
140+
protected Object generateRandomInputValue(MappedFieldType ft) {
141+
assumeFalse("Test implemented in a follow up", true);
142+
return null;
143+
}
138144
}

modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/ScaledFloatFieldMapperTests.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,4 +271,30 @@ public void testRejectIndexOptions() {
271271
containsString("Failed to parse mapping: unknown parameter [index_options] on mapper [field] of type [scaled_float]"));
272272
}
273273

274+
@Override
275+
protected void randomFetchTestFieldConfig(XContentBuilder b) throws IOException {
276+
// Large floats are a terrible idea but the round trip should still work no matter how badly you configure the field
277+
b.field("type", "scaled_float").field("scaling_factor", randomDoubleBetween(0, Float.MAX_VALUE, true));
278+
}
279+
280+
@Override
281+
protected Object generateRandomInputValue(MappedFieldType ft) {
282+
/*
283+
* randomDoubleBetween will smear the random values out across a huge
284+
* range of valid values.
285+
*/
286+
double v = randomDoubleBetween(-Float.MAX_VALUE, Float.MAX_VALUE, true);
287+
switch (between(0, 3)) {
288+
case 0:
289+
return v;
290+
case 1:
291+
return (float) v;
292+
case 2:
293+
return Double.toString(v);
294+
case 3:
295+
return Float.toString((float) v);
296+
default:
297+
throw new IllegalArgumentException();
298+
}
299+
}
274300
}

modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/SearchAsYouTypeFieldMapperTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,4 +745,10 @@ private static PrefixFieldMapper getPrefixFieldMapper(DocumentMapper defaultMapp
745745
assertThat(mapper, instanceOf(PrefixFieldMapper.class));
746746
return (PrefixFieldMapper) mapper;
747747
}
748+
749+
@Override
750+
protected Object generateRandomInputValue(MappedFieldType ft) {
751+
assumeFalse("We don't have doc values or fielddata", true);
752+
return null;
753+
}
748754
}

modules/mapper-extras/src/test/java/org/elasticsearch/index/mapper/TokenCountFieldMapperTests.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,4 +177,20 @@ private ParseContext.Document parseDocument(DocumentMapper mapper, SourceToParse
177177
return mapper.parse(request)
178178
.docs().stream().findFirst().orElseThrow(() -> new IllegalStateException("Test object not parsed"));
179179
}
180+
181+
@Override
182+
protected String generateRandomInputValue(MappedFieldType ft) {
183+
int words = between(1, 1000);
184+
StringBuilder b = new StringBuilder(words * 5);
185+
b.append(randomAlphaOfLength(4));
186+
for (int w = 1; w < words; w++) {
187+
b.append(' ').append(randomAlphaOfLength(4));
188+
}
189+
return b.toString();
190+
}
191+
192+
@Override
193+
protected void randomFetchTestFieldConfig(XContentBuilder b) throws IOException {
194+
b.field("type", "token_count").field("analyzer", "standard");
195+
}
180196
}

plugins/analysis-icu/src/main/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapper.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ public Query regexpQuery(String value, int syntaxFlags, int matchFlags, int maxD
136136
throw new UnsupportedOperationException("[regexp] queries are not supported on [" + CONTENT_TYPE + "] fields.");
137137
}
138138

139-
public static DocValueFormat COLLATE_FORMAT = new DocValueFormat() {
139+
public static final DocValueFormat COLLATE_FORMAT = new DocValueFormat() {
140140
@Override
141141
public String getWriteableName() {
142142
return "collate";

plugins/analysis-icu/src/test/java/org/elasticsearch/index/mapper/ICUCollationKeywordFieldMapperTests.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import com.ibm.icu.text.Collator;
1111
import com.ibm.icu.text.RawCollationKey;
1212
import com.ibm.icu.util.ULocale;
13+
1314
import org.apache.lucene.index.DocValuesType;
1415
import org.apache.lucene.index.IndexOptions;
1516
import org.apache.lucene.index.IndexableField;
@@ -280,4 +281,13 @@ public void testUpdateIgnoreAbove() throws IOException {
280281
assertEquals(0, fields.length);
281282
}
282283

284+
@Override
285+
protected String generateRandomInputValue(MappedFieldType ft) {
286+
assumeFalse("docvalue_fields is broken", true);
287+
// https://github.com/elastic/elasticsearch/issues/70276
288+
/*
289+
* docvalue_fields loads garbage bytes.
290+
*/
291+
return null;
292+
}
283293
}

plugins/mapper-annotated-text/src/internalClusterTest/java/org/elasticsearch/index/mapper/annotatedtext/AnnotatedTextFieldMapperTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
import org.elasticsearch.index.analysis.StandardTokenizerFactory;
3737
import org.elasticsearch.index.analysis.TokenFilterFactory;
3838
import org.elasticsearch.index.mapper.DocumentMapper;
39+
import org.elasticsearch.index.mapper.MappedFieldType;
3940
import org.elasticsearch.index.mapper.MapperParsingException;
4041
import org.elasticsearch.index.mapper.MapperService;
4142
import org.elasticsearch.index.mapper.MapperTestCase;
@@ -552,4 +553,9 @@ public void testAnalyzedFieldPositionIncrementWithoutPositions() {
552553
}
553554
}
554555

556+
@Override
557+
protected Object generateRandomInputValue(MappedFieldType ft) {
558+
assumeFalse("annotated_text doesn't have fielddata so we can't check against anything here.", true);
559+
return null;
560+
}
555561
}

plugins/mapper-murmur3/src/test/java/org/elasticsearch/index/mapper/murmur3/Murmur3FieldMapperTests.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.apache.lucene.index.IndexableField;
1414
import org.elasticsearch.common.xcontent.XContentBuilder;
1515
import org.elasticsearch.index.mapper.DocumentMapper;
16+
import org.elasticsearch.index.mapper.MappedFieldType;
1617
import org.elasticsearch.index.mapper.MapperTestCase;
1718
import org.elasticsearch.index.mapper.ParsedDocument;
1819
import org.elasticsearch.plugin.mapper.MapperMurmur3Plugin;
@@ -56,4 +57,9 @@ public void testDefaults() throws Exception {
5657
assertEquals(DocValuesType.SORTED_NUMERIC, field.fieldType().docValuesType());
5758
}
5859

60+
@Override
61+
protected Object generateRandomInputValue(MappedFieldType ft) {
62+
assumeFalse("Test implemented in a follow up", true);
63+
return null;
64+
}
5965
}

server/src/main/java/org/elasticsearch/index/mapper/RangeType.java

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -546,6 +546,10 @@ public Query intersectsQuery(String field, Object from, Object to, boolean inclu
546546
}
547547
};
548548

549+
public final String name;
550+
private final NumberFieldMapper.NumberType numberType;
551+
public final LengthType lengthType;
552+
549553
RangeType(String name, LengthType lengthType) {
550554
this.name = name;
551555
this.numberType = null;
@@ -699,9 +703,9 @@ public final Mapper.TypeParser parser() {
699703
return new FieldMapper.TypeParser((n, c) -> new RangeFieldMapper.Builder(n, this, c.getSettings()));
700704
}
701705

702-
public final String name;
703-
private final NumberFieldMapper.NumberType numberType;
704-
public final LengthType lengthType;
706+
NumberFieldMapper.NumberType numberType() {
707+
return numberType;
708+
}
705709

706710
public enum LengthType {
707711
FIXED_4 {

0 commit comments

Comments
 (0)