Consolidate code for equals/hashCode testing in central utility class

Currently test that check that equals() and hashCode() are working as expected
for classes implementing them are quiet similar. This change moves common
assertions in this method to a common utility class. In addition, another common
utility function in most of these test classes that creates copies of input
object by running them through a StreamOutput and reading them back in, is moved
to ESTestCase so it can be shared across all these classes.

Closes #20629
This commit is contained in:
Christoph Büscher 2016-10-19 15:22:14 +02:00
parent 2e18f2e818
commit f6f129b21f
24 changed files with 521 additions and 904 deletions

View File

@ -124,6 +124,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
out.writeOptionalString(postFilter); out.writeOptionalString(postFilter);
} }
String field() {
return this.field;
}
/** /**
* The global suggest mode controls what suggested terms are included or * The global suggest mode controls what suggested terms are included or
* controls for what suggest text tokens, terms should be suggested for. * controls for what suggest text tokens, terms should be suggested for.
@ -142,6 +146,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
String suggestMode() {
return suggestMode;
}
/** /**
* Sets how similar the suggested terms at least need to be compared to * Sets how similar the suggested terms at least need to be compared to
* the original suggest text tokens. A value between 0 and 1 can be * the original suggest text tokens. A value between 0 and 1 can be
@ -155,6 +163,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
Float accuracy() {
return this.accuracy;
}
/** /**
* Sets the maximum suggestions to be returned per suggest text term. * Sets the maximum suggestions to be returned per suggest text term.
*/ */
@ -166,6 +178,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
Integer size() {
return size;
}
/** /**
* Sets how to sort the suggest terms per suggest text token. Two * Sets how to sort the suggest terms per suggest text token. Two
* possible values: * possible values:
@ -183,6 +199,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
String sort() {
return sort;
}
/** /**
* Sets what string distance implementation to use for comparing how * Sets what string distance implementation to use for comparing how
* similar suggested terms are. Four possible values can be specified: * similar suggested terms are. Four possible values can be specified:
@ -205,6 +225,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
String stringDistance() {
return stringDistance;
}
/** /**
* Sets the maximum edit distance candidate suggestions can have in * Sets the maximum edit distance candidate suggestions can have in
* order to be considered as a suggestion. Can only be a value between 1 * order to be considered as a suggestion. Can only be a value between 1
@ -219,6 +243,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
Integer maxEdits() {
return maxEdits;
}
/** /**
* A factor that is used to multiply with the size in order to inspect * A factor that is used to multiply with the size in order to inspect
* more candidate suggestions. Can improve accuracy at the cost of * more candidate suggestions. Can improve accuracy at the cost of
@ -229,6 +257,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
Integer maxInspections() {
return maxInspections;
}
/** /**
* Sets a maximum threshold in number of documents a suggest text token * Sets a maximum threshold in number of documents a suggest text token
* can exist in order to be corrected. Can be a relative percentage * can exist in order to be corrected. Can be a relative percentage
@ -245,6 +277,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
Float maxTermFreq() {
return maxTermFreq;
}
/** /**
* Sets the number of minimal prefix characters that must match in order * Sets the number of minimal prefix characters that must match in order
* be a candidate suggestion. Defaults to 1. Increasing this number * be a candidate suggestion. Defaults to 1. Increasing this number
@ -256,6 +292,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
Integer prefixLength() {
return prefixLength;
}
/** /**
* The minimum length a suggest text term must have in order to be * The minimum length a suggest text term must have in order to be
* corrected. Defaults to <tt>4</tt>. * corrected. Defaults to <tt>4</tt>.
@ -265,6 +305,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
Integer minWordLength() {
return minWordLength;
}
/** /**
* Sets a minimal threshold in number of documents a suggested term * Sets a minimal threshold in number of documents a suggested term
* should appear in. This can be specified as an absolute number or as a * should appear in. This can be specified as an absolute number or as a
@ -278,6 +322,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
Float minDocFreq() {
return minDocFreq;
}
/** /**
* Sets a filter (analyzer) that is applied to each of the tokens passed to this candidate generator. * Sets a filter (analyzer) that is applied to each of the tokens passed to this candidate generator.
* This filter is applied to the original token before candidates are generated. * This filter is applied to the original token before candidates are generated.
@ -287,6 +335,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
String preFilter() {
return preFilter;
}
/** /**
* Sets a filter (analyzer) that is applied to each of the generated tokens * Sets a filter (analyzer) that is applied to each of the generated tokens
* before they are passed to the actual phrase scorer. * before they are passed to the actual phrase scorer.
@ -296,6 +348,10 @@ public final class DirectCandidateGeneratorBuilder implements CandidateGenerator
return this; return this;
} }
String postFilter() {
return postFilter;
}
/** /**
* gets the type identifier of this {@link CandidateGenerator} * gets the type identifier of this {@link CandidateGenerator}
*/ */

View File

@ -21,11 +21,14 @@ package org.elasticsearch.action.search;
import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.search.internal.InternalScrollSearchRequest; import org.elasticsearch.search.internal.InternalScrollSearchRequest;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import java.io.IOException; import java.io.IOException;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
public class SearchScrollRequestTests extends ESTestCase { public class SearchScrollRequestTests extends ESTestCase {
public void testSerialization() throws Exception { public void testSerialization() throws Exception {
@ -58,52 +61,7 @@ public class SearchScrollRequestTests extends ESTestCase {
} }
public void testEqualsAndHashcode() { public void testEqualsAndHashcode() {
SearchScrollRequest firstSearchScrollRequest = createSearchScrollRequest(); checkEqualsAndHashCode(createSearchScrollRequest(), SearchScrollRequestTests::copyRequest, SearchScrollRequestTests::mutate);
assertNotNull("search scroll request is equal to null", firstSearchScrollRequest);
assertNotEquals("search scroll request is equal to incompatible type", firstSearchScrollRequest, "");
assertEquals("search scroll request is not equal to self", firstSearchScrollRequest, firstSearchScrollRequest);
assertEquals("same source builder's hashcode returns different values if called multiple times",
firstSearchScrollRequest.hashCode(), firstSearchScrollRequest.hashCode());
SearchScrollRequest secondSearchScrollRequest = copyRequest(firstSearchScrollRequest);
assertEquals("search scroll request is not equal to self", secondSearchScrollRequest, secondSearchScrollRequest);
assertEquals("search scroll request is not equal to its copy", firstSearchScrollRequest, secondSearchScrollRequest);
assertEquals("search scroll request is not symmetric", secondSearchScrollRequest, firstSearchScrollRequest);
assertEquals("search scroll request copy's hashcode is different from original hashcode",
firstSearchScrollRequest.hashCode(), secondSearchScrollRequest.hashCode());
SearchScrollRequest thirdSearchScrollRequest = copyRequest(secondSearchScrollRequest);
assertEquals("search scroll request is not equal to self", thirdSearchScrollRequest, thirdSearchScrollRequest);
assertEquals("search scroll request is not equal to its copy", secondSearchScrollRequest, thirdSearchScrollRequest);
assertEquals("search scroll request copy's hashcode is different from original hashcode",
secondSearchScrollRequest.hashCode(), thirdSearchScrollRequest.hashCode());
assertEquals("equals is not transitive", firstSearchScrollRequest, thirdSearchScrollRequest);
assertEquals("search scroll request copy's hashcode is different from original hashcode",
firstSearchScrollRequest.hashCode(), thirdSearchScrollRequest.hashCode());
assertEquals("equals is not symmetric", thirdSearchScrollRequest, secondSearchScrollRequest);
assertEquals("equals is not symmetric", thirdSearchScrollRequest, firstSearchScrollRequest);
boolean changed = false;
if (randomBoolean()) {
secondSearchScrollRequest.scrollId(randomAsciiOfLengthBetween(3, 10));
if (secondSearchScrollRequest.scrollId().equals(firstSearchScrollRequest.scrollId()) == false) {
changed = true;
}
}
if (randomBoolean()) {
secondSearchScrollRequest.scroll(randomPositiveTimeValue());
if (secondSearchScrollRequest.scroll().equals(firstSearchScrollRequest.scroll()) == false) {
changed = true;
}
}
if (changed) {
assertNotEquals(firstSearchScrollRequest, secondSearchScrollRequest);
assertNotEquals(firstSearchScrollRequest.hashCode(), secondSearchScrollRequest.hashCode());
} else {
assertEquals(firstSearchScrollRequest, secondSearchScrollRequest);
assertEquals(firstSearchScrollRequest.hashCode(), secondSearchScrollRequest.hashCode());
}
} }
public static SearchScrollRequest createSearchScrollRequest() { public static SearchScrollRequest createSearchScrollRequest() {
@ -118,4 +76,13 @@ public class SearchScrollRequestTests extends ESTestCase {
result.scroll(searchScrollRequest.scroll()); result.scroll(searchScrollRequest.scroll());
return result; return result;
} }
private static SearchScrollRequest mutate(SearchScrollRequest original) {
SearchScrollRequest copy = copyRequest(original);
if (randomBoolean()) {
return copy.scrollId(original.scrollId() + "xyz");
} else {
return copy.scroll(new TimeValue(original.scroll().keepAlive().getMillis() + 1));
}
}
} }

View File

@ -19,10 +19,9 @@
package org.elasticsearch.common.geo.builders; package org.elasticsearch.common.geo.builders;
import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.NamedWriteable;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.Writeable.Reader;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
@ -35,11 +34,9 @@ import org.junit.BeforeClass;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import static org.hamcrest.Matchers.equalTo; import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.hamcrest.Matchers.not;
public abstract class AbstractShapeBuilderTestCase<SB extends ShapeBuilder> extends ESTestCase { public abstract class AbstractShapeBuilderTestCase<SB extends ShapeBuilder> extends ESTestCase {
@ -97,11 +94,10 @@ public abstract class AbstractShapeBuilderTestCase<SB extends ShapeBuilder> exte
/** /**
* Test serialization and deserialization of the test shape. * Test serialization and deserialization of the test shape.
*/ */
@SuppressWarnings("unchecked")
public void testSerialization() throws IOException { public void testSerialization() throws IOException {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
SB testShape = createTestShapeBuilder(); SB testShape = createTestShapeBuilder();
SB deserializedShape = (SB) copyShape(testShape); SB deserializedShape = copyShape(testShape);
assertEquals(testShape, deserializedShape); assertEquals(testShape, deserializedShape);
assertEquals(testShape.hashCode(), deserializedShape.hashCode()); assertEquals(testShape.hashCode(), deserializedShape.hashCode());
assertNotSame(testShape, deserializedShape); assertNotSame(testShape, deserializedShape);
@ -111,40 +107,15 @@ public abstract class AbstractShapeBuilderTestCase<SB extends ShapeBuilder> exte
/** /**
* Test equality and hashCode properties * Test equality and hashCode properties
*/ */
@SuppressWarnings("unchecked")
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
SB firstShape = createTestShapeBuilder(); checkEqualsAndHashCode(createTestShapeBuilder(), AbstractShapeBuilderTestCase::copyShape, this::createMutation);
assertFalse("shape is equal to null", firstShape.equals(null));
assertFalse("shape is equal to incompatible type", firstShape.equals(""));
assertTrue("shape is not equal to self", firstShape.equals(firstShape));
assertThat("same shape's hashcode returns different values if called multiple times", firstShape.hashCode(),
equalTo(firstShape.hashCode()));
assertThat("different shapes should not be equal", createMutation(firstShape), not(equalTo(firstShape)));
SB secondShape = (SB) copyShape(firstShape);
assertTrue("shape is not equal to self", secondShape.equals(secondShape));
assertTrue("shape is not equal to its copy", firstShape.equals(secondShape));
assertTrue("equals is not symmetric", secondShape.equals(firstShape));
assertThat("shape copy's hashcode is different from original hashcode", secondShape.hashCode(), equalTo(firstShape.hashCode()));
SB thirdShape = (SB) copyShape(secondShape);
assertTrue("shape is not equal to self", thirdShape.equals(thirdShape));
assertTrue("shape is not equal to its copy", secondShape.equals(thirdShape));
assertThat("shape copy's hashcode is different from original hashcode", secondShape.hashCode(), equalTo(thirdShape.hashCode()));
assertTrue("equals is not transitive", firstShape.equals(thirdShape));
assertThat("shape copy's hashcode is different from original hashcode", firstShape.hashCode(), equalTo(thirdShape.hashCode()));
assertTrue("equals is not symmetric", thirdShape.equals(secondShape));
assertTrue("equals is not symmetric", thirdShape.equals(firstShape));
} }
} }
static ShapeBuilder copyShape(ShapeBuilder original) throws IOException { protected static <T extends NamedWriteable> T copyShape(T original) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) { @SuppressWarnings("unchecked")
original.writeTo(output); Reader<T> reader = (Reader<T>) namedWriteableRegistry.getReader(ShapeBuilder.class, original.getWriteableName());
try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), namedWriteableRegistry)) { return ESTestCase.copyWriteable(original, namedWriteableRegistry, reader);
return namedWriteableRegistry.getReader(ShapeBuilder.class, original.getWriteableName()).read(in);
}
}
} }
} }

View File

@ -20,6 +20,7 @@
package org.elasticsearch.common.geo.builders; package org.elasticsearch.common.geo.builders;
import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Coordinate;
import org.elasticsearch.common.unit.DistanceUnit; import org.elasticsearch.common.unit.DistanceUnit;
import java.io.IOException; import java.io.IOException;
@ -37,7 +38,7 @@ public class CircleBuilderTests extends AbstractShapeBuilderTestCase<CircleBuild
} }
static CircleBuilder mutate(CircleBuilder original) throws IOException { static CircleBuilder mutate(CircleBuilder original) throws IOException {
CircleBuilder mutation = (CircleBuilder) copyShape(original); CircleBuilder mutation = copyShape(original);
double radius = original.radius(); double radius = original.radius();
DistanceUnit unit = original.unit(); DistanceUnit unit = original.unit();

View File

@ -20,10 +20,7 @@ package org.elasticsearch.index.query;
import org.apache.lucene.search.join.ScoreMode; import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
@ -56,6 +53,7 @@ import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not; import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.notNullValue; import static org.hamcrest.Matchers.notNullValue;
@ -111,31 +109,7 @@ public class InnerHitBuilderTests extends ESTestCase {
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
InnerHitBuilder firstInnerHit = randomInnerHits(); checkEqualsAndHashCode(randomInnerHits(), InnerHitBuilderTests::serializedCopy, InnerHitBuilderTests::mutate);
assertFalse("inner hit is equal to null", firstInnerHit.equals(null));
assertFalse("inner hit is equal to incompatible type", firstInnerHit.equals(""));
assertTrue("inner it is not equal to self", firstInnerHit.equals(firstInnerHit));
assertThat("same inner hit's hashcode returns different values if called multiple times", firstInnerHit.hashCode(),
equalTo(firstInnerHit.hashCode()));
assertThat("different inner hits should not be equal", mutate(serializedCopy(firstInnerHit)), not(equalTo(firstInnerHit)));
InnerHitBuilder secondBuilder = serializedCopy(firstInnerHit);
assertTrue("inner hit is not equal to self", secondBuilder.equals(secondBuilder));
assertTrue("inner hit is not equal to its copy", firstInnerHit.equals(secondBuilder));
assertTrue("equals is not symmetric", secondBuilder.equals(firstInnerHit));
assertThat("inner hits copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
equalTo(firstInnerHit.hashCode()));
InnerHitBuilder thirdBuilder = serializedCopy(secondBuilder);
assertTrue("inner hit is not equal to self", thirdBuilder.equals(thirdBuilder));
assertTrue("inner hit is not equal to its copy", secondBuilder.equals(thirdBuilder));
assertThat("inner hit copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
equalTo(thirdBuilder.hashCode()));
assertTrue("equals is not transitive", firstInnerHit.equals(thirdBuilder));
assertThat("inner hit copy's hashcode is different from original hashcode", firstInnerHit.hashCode(),
equalTo(thirdBuilder.hashCode()));
assertTrue("equals is not symmetric", thirdBuilder.equals(secondBuilder));
assertTrue("equals is not symmetric", thirdBuilder.equals(firstInnerHit));
} }
} }
@ -277,90 +251,71 @@ public class InnerHitBuilderTests extends ESTestCase {
assertThat(copy, not(equalTo(original))); assertThat(copy, not(equalTo(original)));
} }
static InnerHitBuilder mutate(InnerHitBuilder instance) throws IOException { static InnerHitBuilder mutate(InnerHitBuilder original) throws IOException {
int surprise = randomIntBetween(0, 11); final InnerHitBuilder copy = serializedCopy(original);
switch (surprise) { List<Runnable> modifiers = new ArrayList<>(12);
case 0: modifiers.add(() -> copy.setFrom(randomValueOtherThan(copy.getFrom(), () -> randomIntBetween(0, 128))));
instance.setFrom(randomValueOtherThan(instance.getFrom(), () -> randomIntBetween(0, 128))); modifiers.add(() -> copy.setSize(randomValueOtherThan(copy.getSize(), () -> randomIntBetween(0, 128))));
break; modifiers.add(() -> copy.setExplain(!copy.isExplain()));
case 1: modifiers.add(() -> copy.setVersion(!copy.isVersion()));
instance.setSize(randomValueOtherThan(instance.getSize(), () -> randomIntBetween(0, 128))); modifiers.add(() -> copy.setTrackScores(!copy.isTrackScores()));
break; modifiers.add(() -> copy.setName(randomValueOtherThan(copy.getName(), () -> randomAsciiOfLengthBetween(1, 16))));
case 2: modifiers.add(() -> {
instance.setExplain(!instance.isExplain());
break;
case 3:
instance.setVersion(!instance.isVersion());
break;
case 4:
instance.setTrackScores(!instance.isTrackScores());
break;
case 5:
instance.setName(randomValueOtherThan(instance.getName(), () -> randomAsciiOfLengthBetween(1, 16)));
break;
case 6:
if (randomBoolean()) { if (randomBoolean()) {
instance.setDocValueFields(randomValueOtherThan(instance.getDocValueFields(), () -> { copy.setDocValueFields(randomValueOtherThan(copy.getDocValueFields(), () -> {
return randomListStuff(16, () -> randomAsciiOfLengthBetween(1, 16)); return randomListStuff(16, () -> randomAsciiOfLengthBetween(1, 16));
})); }));
} else { } else {
instance.addDocValueField(randomAsciiOfLengthBetween(1, 16)); copy.addDocValueField(randomAsciiOfLengthBetween(1, 16));
} }
break; });
case 7: modifiers.add(() -> {
if (randomBoolean()) { if (randomBoolean()) {
instance.setScriptFields(randomValueOtherThan(instance.getScriptFields(), () -> { copy.setScriptFields(randomValueOtherThan(copy.getScriptFields(), () -> {
return new HashSet<>(randomListStuff(16, InnerHitBuilderTests::randomScript));})); return new HashSet<>(randomListStuff(16, InnerHitBuilderTests::randomScript));
}));
} else { } else {
SearchSourceBuilder.ScriptField script = randomScript(); SearchSourceBuilder.ScriptField script = randomScript();
instance.addScriptField(script.fieldName(), script.script()); copy.addScriptField(script.fieldName(), script.script());
} }
break; });
case 8: modifiers.add(() -> copy.setFetchSourceContext(randomValueOtherThan(copy.getFetchSourceContext(), () -> {
instance.setFetchSourceContext(randomValueOtherThan(instance.getFetchSourceContext(), () -> {
FetchSourceContext randomFetchSourceContext; FetchSourceContext randomFetchSourceContext;
if (randomBoolean()) { if (randomBoolean()) {
randomFetchSourceContext = new FetchSourceContext(randomBoolean()); randomFetchSourceContext = new FetchSourceContext(randomBoolean());
} else { } else {
randomFetchSourceContext = new FetchSourceContext(true, randomFetchSourceContext = new FetchSourceContext(true, generateRandomStringArray(12, 16, false),
generateRandomStringArray(12, 16, false), generateRandomStringArray(12, 16, false));
generateRandomStringArray(12, 16, false)
);
} }
return randomFetchSourceContext; return randomFetchSourceContext;
})); })));
break; modifiers.add(() -> {
case 9:
if (randomBoolean()) { if (randomBoolean()) {
final List<SortBuilder<?>> sortBuilders = randomValueOtherThan(instance.getSorts(), () -> { final List<SortBuilder<?>> sortBuilders = randomValueOtherThan(copy.getSorts(), () -> {
List<SortBuilder<?>> builders = randomListStuff(16, List<SortBuilder<?>> builders = randomListStuff(16,
() -> SortBuilders.fieldSort(randomAsciiOfLengthBetween(5, 20)).order(randomFrom(SortOrder.values()))); () -> SortBuilders.fieldSort(randomAsciiOfLengthBetween(5, 20)).order(randomFrom(SortOrder.values())));
return builders; return builders;
}); });
instance.setSorts(sortBuilders); copy.setSorts(sortBuilders);
} else { } else {
instance.addSort(SortBuilders.fieldSort(randomAsciiOfLengthBetween(5, 20))); copy.addSort(SortBuilders.fieldSort(randomAsciiOfLengthBetween(5, 20)));
} }
break; });
case 10: modifiers.add(() -> copy
instance.setHighlightBuilder(randomValueOtherThan(instance.getHighlightBuilder(), .setHighlightBuilder(randomValueOtherThan(copy.getHighlightBuilder(), HighlightBuilderTests::randomHighlighterBuilder)));
HighlightBuilderTests::randomHighlighterBuilder)); modifiers.add(() -> {
break; if (copy.getStoredFieldsContext() == null || randomBoolean()) {
case 11: List<String> previous = copy.getStoredFieldsContext() == null ?
if (instance.getStoredFieldsContext() == null || randomBoolean()) { Collections.emptyList() : copy.getStoredFieldsContext().fieldNames();
List<String> previous = instance.getStoredFieldsContext() == null ?
Collections.emptyList() : instance.getStoredFieldsContext().fieldNames();
List<String> newValues = randomValueOtherThan(previous, List<String> newValues = randomValueOtherThan(previous,
() -> randomListStuff(1, 16, () -> randomAsciiOfLengthBetween(1, 16))); () -> randomListStuff(1, 16, () -> randomAsciiOfLengthBetween(1, 16)));
instance.setStoredFieldNames(newValues); copy.setStoredFieldNames(newValues);
} else { } else {
instance.getStoredFieldsContext().addFieldName(randomAsciiOfLengthBetween(1, 16)); copy.getStoredFieldsContext().addFieldName(randomAsciiOfLengthBetween(1, 16));
} }
break; });
default: randomFrom(modifiers).run();
throw new IllegalStateException("unexpected surprise [" + surprise + "]"); return copy;
}
return instance;
} }
static SearchSourceBuilder.ScriptField randomScript() { static SearchSourceBuilder.ScriptField randomScript() {
@ -391,12 +346,7 @@ public class InnerHitBuilderTests extends ESTestCase {
} }
private static InnerHitBuilder serializedCopy(InnerHitBuilder original) throws IOException { private static InnerHitBuilder serializedCopy(InnerHitBuilder original) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) { return ESTestCase.copyWriteable(original, namedWriteableRegistry, InnerHitBuilder::new);
original.writeTo(output);
try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), namedWriteableRegistry)) {
return new InnerHitBuilder(in);
}
}
} }
} }

View File

@ -26,9 +26,13 @@ import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.unit.TimeValue; import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.util.ArrayUtils;
import java.io.IOException; import java.io.IOException;
import java.util.Arrays; import java.util.ArrayList;
import java.util.List;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
public class SearchRequestTests extends AbstractSearchTestCase { public class SearchRequestTests extends AbstractSearchTestCase {
@ -77,95 +81,31 @@ public class SearchRequestTests extends AbstractSearchTestCase {
} }
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
SearchRequest firstSearchRequest = createSearchRequest(); checkEqualsAndHashCode(createSearchRequest(), SearchRequestTests::copyRequest, this::mutate);
assertNotNull("search request is equal to null", firstSearchRequest);
assertNotEquals("search request is equal to incompatible type", firstSearchRequest, "");
assertEquals("search request is not equal to self", firstSearchRequest, firstSearchRequest);
assertEquals("same source builder's hashcode returns different values if called multiple times",
firstSearchRequest.hashCode(), firstSearchRequest.hashCode());
SearchRequest secondSearchRequest = copyRequest(firstSearchRequest);
assertEquals("search request is not equal to self", secondSearchRequest, secondSearchRequest);
assertEquals("search request is not equal to its copy", firstSearchRequest, secondSearchRequest);
assertEquals("search request is not symmetric", secondSearchRequest, firstSearchRequest);
assertEquals("search request copy's hashcode is different from original hashcode",
firstSearchRequest.hashCode(), secondSearchRequest.hashCode());
SearchRequest thirdSearchRequest = copyRequest(secondSearchRequest);
assertEquals("search request is not equal to self", thirdSearchRequest, thirdSearchRequest);
assertEquals("search request is not equal to its copy", secondSearchRequest, thirdSearchRequest);
assertEquals("search request copy's hashcode is different from original hashcode",
secondSearchRequest.hashCode(), thirdSearchRequest.hashCode());
assertEquals("equals is not transitive", firstSearchRequest, thirdSearchRequest);
assertEquals("search request copy's hashcode is different from original hashcode",
firstSearchRequest.hashCode(), thirdSearchRequest.hashCode());
assertEquals("equals is not symmetric", thirdSearchRequest, secondSearchRequest);
assertEquals("equals is not symmetric", thirdSearchRequest, firstSearchRequest);
boolean changed = false;
if (randomBoolean()) {
secondSearchRequest.indices(generateRandomStringArray(10, 10, false, false));
if (Arrays.equals(secondSearchRequest.indices(), firstSearchRequest.indices()) == false) {
changed = true;
}
}
if (randomBoolean()) {
secondSearchRequest.indicesOptions(
IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean()));
if (secondSearchRequest.indicesOptions().equals(firstSearchRequest.indicesOptions()) == false) {
changed = true;
}
}
if (randomBoolean()) {
secondSearchRequest.types(generateRandomStringArray(10, 10, false, false));
if (Arrays.equals(secondSearchRequest.types(), firstSearchRequest.types()) == false) {
changed = true;
}
}
if (randomBoolean()) {
secondSearchRequest.preference(randomAsciiOfLengthBetween(3, 10));
if (secondSearchRequest.preference().equals(firstSearchRequest.preference()) == false) {
changed = true;
}
}
if (randomBoolean()) {
secondSearchRequest.routing(randomAsciiOfLengthBetween(3, 10));
if (secondSearchRequest.routing().equals(firstSearchRequest.routing()) == false) {
changed = true;
}
}
if (randomBoolean()) {
secondSearchRequest.requestCache(randomBoolean());
if (secondSearchRequest.requestCache().equals(firstSearchRequest.requestCache()) == false) {
changed = true;
}
}
if (randomBoolean()) {
secondSearchRequest.scroll(randomPositiveTimeValue());
if (secondSearchRequest.scroll().equals(firstSearchRequest.scroll()) == false) {
changed = true;
}
}
if (randomBoolean()) {
secondSearchRequest.searchType(randomFrom(SearchType.values()));
if (secondSearchRequest.searchType() != firstSearchRequest.searchType()) {
changed = true;
}
}
if (randomBoolean()) {
secondSearchRequest.source(createSearchSourceBuilder());
if (secondSearchRequest.source().equals(firstSearchRequest.source()) == false) {
changed = true;
}
} }
if (changed) { private SearchRequest mutate(SearchRequest searchRequest) throws IOException {
assertNotEquals(firstSearchRequest, secondSearchRequest); SearchRequest mutation = copyRequest(searchRequest);
assertNotEquals(firstSearchRequest.hashCode(), secondSearchRequest.hashCode()); List<Runnable> mutators = new ArrayList<>();
} else { mutators.add(() -> mutation.indices(ArrayUtils.concat(searchRequest.indices(), new String[] { randomAsciiOfLength(10) })));
assertEquals(firstSearchRequest, secondSearchRequest); mutators.add(() -> mutation.indicesOptions(randomValueOtherThan(searchRequest.indicesOptions(),
assertEquals(firstSearchRequest.hashCode(), secondSearchRequest.hashCode()); () -> IndicesOptions.fromOptions(randomBoolean(), randomBoolean(), randomBoolean(), randomBoolean()))));
mutators.add(() -> mutation.types(ArrayUtils.concat(searchRequest.types(), new String[] { randomAsciiOfLength(10) })));
mutators.add(() -> mutation.preference(randomValueOtherThan(searchRequest.preference(), () -> randomAsciiOfLengthBetween(3, 10))));
mutators.add(() -> mutation.routing(randomValueOtherThan(searchRequest.routing(), () -> randomAsciiOfLengthBetween(3, 10))));
mutators.add(() -> mutation.requestCache((randomValueOtherThan(searchRequest.requestCache(), () -> randomBoolean()))));
mutators.add(() -> mutation
.scroll(randomValueOtherThan(searchRequest.scroll(), () -> new Scroll(new TimeValue(randomPositiveLong() % 100000)))));
mutators.add(() -> mutation.searchType(randomValueOtherThan(searchRequest.searchType(), () -> randomFrom(SearchType.values()))));
mutators.add(() -> mutation.source(randomValueOtherThan(searchRequest.source(), () -> {
try {
return createSearchSourceBuilder();
} catch (IOException e) {
throw new RuntimeException(e);
} }
})));
randomFrom(mutators).run();
return mutation;
} }
private static SearchRequest copyRequest(SearchRequest searchRequest) throws IOException { private static SearchRequest copyRequest(SearchRequest searchRequest) throws IOException {

View File

@ -31,11 +31,11 @@ import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentParser; import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.env.Environment; import org.elasticsearch.env.Environment;
import org.elasticsearch.test.AbstractQueryTestCase;
import org.elasticsearch.index.query.QueryParseContext; import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.indices.IndicesModule; import org.elasticsearch.indices.IndicesModule;
import org.elasticsearch.indices.query.IndicesQueriesRegistry; import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.search.SearchModule; import org.elasticsearch.search.SearchModule;
import org.elasticsearch.test.AbstractQueryTestCase;
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import java.io.IOException; import java.io.IOException;
@ -44,7 +44,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.hamcrest.Matchers.equalTo; import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
public abstract class BaseAggregationTestCase<AB extends AbstractAggregationBuilder<AB>> extends ESTestCase { public abstract class BaseAggregationTestCase<AB extends AbstractAggregationBuilder<AB>> extends ESTestCase {
@ -72,6 +72,7 @@ public abstract class BaseAggregationTestCase<AB extends AbstractAggregationBuil
/** /**
* Setup for the whole base test class. * Setup for the whole base test class.
*/ */
@Override
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
Settings settings = Settings.builder() Settings settings = Settings.builder()
@ -148,28 +149,9 @@ public abstract class BaseAggregationTestCase<AB extends AbstractAggregationBuil
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
AB firstAgg = createTestAggregatorBuilder(); // TODO we only change name and boost, we should extend by any sub-test supplying a "mutate" method that randomly changes one
assertFalse("aggregation is equal to null", firstAgg.equals(null)); // aspect of the object under test
assertFalse("aggregation is equal to incompatible type", firstAgg.equals("")); checkEqualsAndHashCode(createTestAggregatorBuilder(), this::copyAggregation);
assertTrue("aggregation is not equal to self", firstAgg.equals(firstAgg));
assertThat("same aggregation's hashcode returns different values if called multiple times", firstAgg.hashCode(),
equalTo(firstAgg.hashCode()));
AB secondQuery = copyAggregation(firstAgg);
assertTrue("aggregation is not equal to self", secondQuery.equals(secondQuery));
assertTrue("aggregation is not equal to its copy", firstAgg.equals(secondQuery));
assertTrue("equals is not symmetric", secondQuery.equals(firstAgg));
assertThat("aggregation copy's hashcode is different from original hashcode", secondQuery.hashCode(), equalTo(firstAgg.hashCode()));
AB thirdQuery = copyAggregation(secondQuery);
assertTrue("aggregation is not equal to self", thirdQuery.equals(thirdQuery));
assertTrue("aggregation is not equal to its copy", secondQuery.equals(thirdQuery));
assertThat("aggregation copy's hashcode is different from original hashcode", secondQuery.hashCode(),
equalTo(thirdQuery.hashCode()));
assertTrue("equals is not transitive", firstAgg.equals(thirdQuery));
assertThat("aggregation copy's hashcode is different from original hashcode", firstAgg.hashCode(), equalTo(thirdQuery.hashCode()));
assertTrue("equals is not symmetric", thirdQuery.equals(secondQuery));
assertTrue("equals is not symmetric", thirdQuery.equals(firstAgg));
} }
// we use the streaming infra to create a copy of the query provided as // we use the streaming infra to create a copy of the query provided as

View File

@ -46,7 +46,7 @@ import java.util.Collections;
import java.util.List; import java.util.List;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.hamcrest.Matchers.equalTo; import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
public abstract class BasePipelineAggregationTestCase<AF extends AbstractPipelineAggregationBuilder<AF>> extends ESTestCase { public abstract class BasePipelineAggregationTestCase<AF extends AbstractPipelineAggregationBuilder<AF>> extends ESTestCase {
@ -73,6 +73,7 @@ public abstract class BasePipelineAggregationTestCase<AF extends AbstractPipelin
/** /**
* Setup for the whole base test class. * Setup for the whole base test class.
*/ */
@Override
public void setUp() throws Exception { public void setUp() throws Exception {
super.setUp(); super.setUp();
Settings settings = Settings.builder() Settings settings = Settings.builder()
@ -153,28 +154,9 @@ public abstract class BasePipelineAggregationTestCase<AF extends AbstractPipelin
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
AF firstAgg = createTestAggregatorFactory(); // TODO we only change name and boost, we should extend by any sub-test supplying a "mutate" method that randomly changes one
assertFalse("aggregation is equal to null", firstAgg.equals(null)); // aspect of the object under test
assertFalse("aggregation is equal to incompatible type", firstAgg.equals("")); checkEqualsAndHashCode(createTestAggregatorFactory(), this::copyAggregation);
assertTrue("aggregation is not equal to self", firstAgg.equals(firstAgg));
assertThat("same aggregation's hashcode returns different values if called multiple times", firstAgg.hashCode(),
equalTo(firstAgg.hashCode()));
AF secondQuery = copyAggregation(firstAgg);
assertTrue("aggregation is not equal to self", secondQuery.equals(secondQuery));
assertTrue("aggregation is not equal to its copy", firstAgg.equals(secondQuery));
assertTrue("equals is not symmetric", secondQuery.equals(firstAgg));
assertThat("aggregation copy's hashcode is different from original hashcode", secondQuery.hashCode(), equalTo(firstAgg.hashCode()));
AF thirdQuery = copyAggregation(secondQuery);
assertTrue("aggregation is not equal to self", thirdQuery.equals(thirdQuery));
assertTrue("aggregation is not equal to its copy", secondQuery.equals(thirdQuery));
assertThat("aggregation copy's hashcode is different from original hashcode", secondQuery.hashCode(),
equalTo(thirdQuery.hashCode()));
assertTrue("equals is not transitive", firstAgg.equals(thirdQuery));
assertThat("aggregation copy's hashcode is different from original hashcode", firstAgg.hashCode(), equalTo(thirdQuery.hashCode()));
assertTrue("equals is not symmetric", thirdQuery.equals(secondQuery));
assertTrue("equals is not symmetric", thirdQuery.equals(firstAgg));
} }
// we use the streaming infra to create a copy of the query provided as // we use the streaming infra to create a copy of the query provided as

View File

@ -40,6 +40,8 @@ import org.elasticsearch.search.rescore.QueryRescorerBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder; import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.ScoreSortBuilder; import org.elasticsearch.search.sort.ScoreSortBuilder;
import org.elasticsearch.search.sort.SortOrder; import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.test.ESTestCase;
import org.elasticsearch.test.EqualsHashCodeTestUtils;
import java.io.IOException; import java.io.IOException;
@ -96,40 +98,13 @@ public class SearchSourceBuilderTests extends AbstractSearchTestCase {
} }
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
SearchSourceBuilder firstBuilder = createSearchSourceBuilder(); // TODO add test checking that changing any member of this class produces an object that is not equal to the original
assertNotNull("source builder is equal to null", firstBuilder); EqualsHashCodeTestUtils.checkEqualsAndHashCode(createSearchSourceBuilder(), this::copyBuilder);
assertFalse("source builder is equal to incompatible type", firstBuilder.equals(""));
assertTrue("source builder is not equal to self", firstBuilder.equals(firstBuilder));
assertThat("same source builder's hashcode returns different values if called multiple times", firstBuilder.hashCode(),
equalTo(firstBuilder.hashCode()));
SearchSourceBuilder secondBuilder = copyBuilder(firstBuilder);
assertTrue("source builder is not equal to self", secondBuilder.equals(secondBuilder));
assertTrue("source builder is not equal to its copy", firstBuilder.equals(secondBuilder));
assertTrue("source builder is not symmetric", secondBuilder.equals(firstBuilder));
assertThat("source builder copy's hashcode is different from original hashcode",
secondBuilder.hashCode(), equalTo(firstBuilder.hashCode()));
SearchSourceBuilder thirdBuilder = copyBuilder(secondBuilder);
assertTrue("source builder is not equal to self", thirdBuilder.equals(thirdBuilder));
assertTrue("source builder is not equal to its copy", secondBuilder.equals(thirdBuilder));
assertThat("source builder copy's hashcode is different from original hashcode",
secondBuilder.hashCode(), equalTo(thirdBuilder.hashCode()));
assertTrue("equals is not transitive", firstBuilder.equals(thirdBuilder));
assertThat("source builder copy's hashcode is different from original hashcode",
firstBuilder.hashCode(), equalTo(thirdBuilder.hashCode()));
assertTrue("equals is not symmetric", thirdBuilder.equals(secondBuilder));
assertTrue("equals is not symmetric", thirdBuilder.equals(firstBuilder));
} }
//we use the streaming infra to create a copy of the builder provided as argument //we use the streaming infra to create a copy of the builder provided as argument
private SearchSourceBuilder copyBuilder(SearchSourceBuilder builder) throws IOException { private SearchSourceBuilder copyBuilder(SearchSourceBuilder original) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) { return ESTestCase.copyWriteable(original, namedWriteableRegistry, SearchSourceBuilder::new);
builder.writeTo(output);
try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), namedWriteableRegistry)) {
return new SearchSourceBuilder(in);
}
}
} }
public void testParseIncludeExclude() throws IOException { public void testParseIncludeExclude() throws IOException {

View File

@ -25,7 +25,6 @@ import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
@ -69,8 +68,8 @@ import java.util.function.BiConsumer;
import java.util.function.Function; import java.util.function.Function;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
public class HighlightBuilderTests extends ESTestCase { public class HighlightBuilderTests extends ESTestCase {
@ -112,31 +111,7 @@ public class HighlightBuilderTests extends ESTestCase {
*/ */
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
HighlightBuilder firstBuilder = randomHighlighterBuilder(); checkEqualsAndHashCode(randomHighlighterBuilder(), HighlightBuilderTests::serializedCopy, HighlightBuilderTests::mutate);
assertFalse("highlighter is equal to null", firstBuilder.equals(null));
assertFalse("highlighter is equal to incompatible type", firstBuilder.equals(""));
assertTrue("highlighter is not equal to self", firstBuilder.equals(firstBuilder));
assertThat("same highlighter's hashcode returns different values if called multiple times", firstBuilder.hashCode(),
equalTo(firstBuilder.hashCode()));
assertThat("different highlighters should not be equal", mutate(firstBuilder), not(equalTo(firstBuilder)));
HighlightBuilder secondBuilder = serializedCopy(firstBuilder);
assertTrue("highlighter is not equal to self", secondBuilder.equals(secondBuilder));
assertTrue("highlighter is not equal to its copy", firstBuilder.equals(secondBuilder));
assertTrue("equals is not symmetric", secondBuilder.equals(firstBuilder));
assertThat("highlighter copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
equalTo(firstBuilder.hashCode()));
HighlightBuilder thirdBuilder = serializedCopy(secondBuilder);
assertTrue("highlighter is not equal to self", thirdBuilder.equals(thirdBuilder));
assertTrue("highlighter is not equal to its copy", secondBuilder.equals(thirdBuilder));
assertThat("highlighter copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
equalTo(thirdBuilder.hashCode()));
assertTrue("equals is not transitive", firstBuilder.equals(thirdBuilder));
assertThat("highlighter copy's hashcode is different from original hashcode", firstBuilder.hashCode(),
equalTo(thirdBuilder.hashCode()));
assertTrue("equals is not symmetric", thirdBuilder.equals(secondBuilder));
assertTrue("equals is not symmetric", thirdBuilder.equals(firstBuilder));
} }
} }
@ -332,7 +307,7 @@ public class HighlightBuilderTests extends ESTestCase {
String[] copy = Arrays.copyOf(fieldBuilder.matchedFields, fieldBuilder.matchedFields.length); String[] copy = Arrays.copyOf(fieldBuilder.matchedFields, fieldBuilder.matchedFields.length);
Arrays.sort(copy); Arrays.sort(copy);
assertArrayEquals(copy, assertArrayEquals(copy,
new TreeSet<String>(fieldOptions.matchedFields()).toArray(new String[fieldOptions.matchedFields().size()])); new TreeSet<>(fieldOptions.matchedFields()).toArray(new String[fieldOptions.matchedFields().size()]));
} else { } else {
assertNull(fieldOptions.matchedFields()); assertNull(fieldOptions.matchedFields());
} }
@ -533,7 +508,7 @@ public class HighlightBuilderTests extends ESTestCase {
return testHighlighter; return testHighlighter;
} }
@SuppressWarnings({ "rawtypes", "unchecked" }) @SuppressWarnings({ "rawtypes"})
private static void setRandomCommonOptions(AbstractHighlighterBuilder highlightBuilder) { private static void setRandomCommonOptions(AbstractHighlighterBuilder highlightBuilder) {
if (randomBoolean()) { if (randomBoolean()) {
// need to set this together, otherwise parsing will complain // need to set this together, otherwise parsing will complain
@ -597,7 +572,7 @@ public class HighlightBuilderTests extends ESTestCase {
} }
if (randomBoolean()) { if (randomBoolean()) {
int items = randomIntBetween(0, 5); int items = randomIntBetween(0, 5);
Map<String, Object> options = new HashMap<String, Object>(items); Map<String, Object> options = new HashMap<>(items);
for (int i = 0; i < items; i++) { for (int i = 0; i < items; i++) {
Object value = null; Object value = null;
switch (randomInt(2)) { switch (randomInt(2)) {
@ -670,7 +645,7 @@ public class HighlightBuilderTests extends ESTestCase {
break; break;
case 15: case 15:
int items = 6; int items = 6;
Map<String, Object> options = new HashMap<String, Object>(items); Map<String, Object> options = new HashMap<>(items);
for (int i = 0; i < items; i++) { for (int i = 0; i < items; i++) {
options.put(randomAsciiOfLengthBetween(1, 10), randomAsciiOfLengthBetween(1, 10)); options.put(randomAsciiOfLengthBetween(1, 10), randomAsciiOfLengthBetween(1, 10));
} }
@ -696,7 +671,7 @@ public class HighlightBuilderTests extends ESTestCase {
*/ */
private static String[] randomStringArray(int minSize, int maxSize) { private static String[] randomStringArray(int minSize, int maxSize) {
int size = randomIntBetween(minSize, maxSize); int size = randomIntBetween(minSize, maxSize);
Set<String> randomStrings = new HashSet<String>(size); Set<String> randomStrings = new HashSet<>(size);
for (int f = 0; f < size; f++) { for (int f = 0; f < size; f++) {
randomStrings.add(randomAsciiOfLengthBetween(3, 10)); randomStrings.add(randomAsciiOfLengthBetween(3, 10));
} }
@ -738,11 +713,6 @@ public class HighlightBuilderTests extends ESTestCase {
} }
private static HighlightBuilder serializedCopy(HighlightBuilder original) throws IOException { private static HighlightBuilder serializedCopy(HighlightBuilder original) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) { return ESTestCase.copyWriteable(original, namedWriteableRegistry, HighlightBuilder::new);
original.writeTo(output);
try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), namedWriteableRegistry)) {
return new HighlightBuilder(in);
}
}
} }
} }

View File

@ -25,10 +25,7 @@ import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
@ -56,8 +53,7 @@ import org.junit.BeforeClass;
import java.io.IOException; import java.io.IOException;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.hamcrest.Matchers.equalTo; import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.hamcrest.Matchers.not;
public class QueryRescoreBuilderTests extends ESTestCase { public class QueryRescoreBuilderTests extends ESTestCase {
@ -87,7 +83,7 @@ public class QueryRescoreBuilderTests extends ESTestCase {
public void testSerialization() throws IOException { public void testSerialization() throws IOException {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
RescoreBuilder<?> original = randomRescoreBuilder(); RescoreBuilder<?> original = randomRescoreBuilder();
RescoreBuilder<?> deserialized = serializedCopy(original); RescoreBuilder<?> deserialized = copy(original);
assertEquals(deserialized, original); assertEquals(deserialized, original);
assertEquals(deserialized.hashCode(), original.hashCode()); assertEquals(deserialized.hashCode(), original.hashCode());
assertNotSame(deserialized, original); assertNotSame(deserialized, original);
@ -99,34 +95,15 @@ public class QueryRescoreBuilderTests extends ESTestCase {
*/ */
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
RescoreBuilder<?> firstBuilder = randomRescoreBuilder(); checkEqualsAndHashCode(randomRescoreBuilder(), this::copy, QueryRescoreBuilderTests::mutate);
assertFalse("rescore builder is equal to null", firstBuilder.equals(null));
assertFalse("rescore builder is equal to incompatible type", firstBuilder.equals(""));
assertTrue("rescore builder is not equal to self", firstBuilder.equals(firstBuilder));
assertThat("same rescore builder's hashcode returns different values if called multiple times", firstBuilder.hashCode(),
equalTo(firstBuilder.hashCode()));
assertThat("different rescore builder should not be equal", mutate(firstBuilder), not(equalTo(firstBuilder)));
RescoreBuilder<?> secondBuilder = serializedCopy(firstBuilder);
assertTrue("rescore builder is not equal to self", secondBuilder.equals(secondBuilder));
assertTrue("rescore builder is not equal to its copy", firstBuilder.equals(secondBuilder));
assertTrue("equals is not symmetric", secondBuilder.equals(firstBuilder));
assertThat("rescore builder copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
equalTo(firstBuilder.hashCode()));
RescoreBuilder<?> thirdBuilder = serializedCopy(secondBuilder);
assertTrue("rescore builder is not equal to self", thirdBuilder.equals(thirdBuilder));
assertTrue("rescore builder is not equal to its copy", secondBuilder.equals(thirdBuilder));
assertThat("rescore builder copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
equalTo(thirdBuilder.hashCode()));
assertTrue("equals is not transitive", firstBuilder.equals(thirdBuilder));
assertThat("rescore builder copy's hashcode is different from original hashcode", firstBuilder.hashCode(),
equalTo(thirdBuilder.hashCode()));
assertTrue("equals is not symmetric", thirdBuilder.equals(secondBuilder));
assertTrue("equals is not symmetric", thirdBuilder.equals(firstBuilder));
} }
} }
private RescoreBuilder<?> copy(RescoreBuilder<?> original) throws IOException {
return copyWriteable(original, namedWriteableRegistry,
namedWriteableRegistry.getReader(RescoreBuilder.class, original.getWriteableName()));
}
/** /**
* creates random rescorer, renders it to xContent and back to new instance that should be equal to original * creates random rescorer, renders it to xContent and back to new instance that should be equal to original
*/ */
@ -282,7 +259,7 @@ public class QueryRescoreBuilderTests extends ESTestCase {
} }
private static RescoreBuilder<?> mutate(RescoreBuilder<?> original) throws IOException { private static RescoreBuilder<?> mutate(RescoreBuilder<?> original) throws IOException {
RescoreBuilder<?> mutation = serializedCopy(original); RescoreBuilder<?> mutation = ESTestCase.copyWriteable(original, namedWriteableRegistry, QueryRescorerBuilder::new);
if (randomBoolean()) { if (randomBoolean()) {
Integer windowSize = original.windowSize(); Integer windowSize = original.windowSize();
if (windowSize != null) { if (windowSize != null) {
@ -339,14 +316,4 @@ public class QueryRescoreBuilderTests extends ESTestCase {
} }
return rescorer; return rescorer;
} }
private static RescoreBuilder<?> serializedCopy(RescoreBuilder<?> original) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) {
output.writeNamedWriteable(original);
try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), namedWriteableRegistry)) {
return in.readNamedWriteable(RescoreBuilder.class);
}
}
}
} }

View File

@ -21,10 +21,7 @@ package org.elasticsearch.search.searchafter;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.geo.GeoPoint; import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.text.Text; import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
@ -41,7 +38,9 @@ import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
import java.io.IOException; import java.io.IOException;
import static org.hamcrest.Matchers.equalTo; import java.util.Collections;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
public class SearchAfterBuilderTests extends ESTestCase { public class SearchAfterBuilderTests extends ESTestCase {
private static final int NUMBER_OF_TESTBUILDERS = 20; private static final int NUMBER_OF_TESTBUILDERS = 20;
@ -62,7 +61,7 @@ public class SearchAfterBuilderTests extends ESTestCase {
indicesQueriesRegistry = null; indicesQueriesRegistry = null;
} }
private SearchAfterBuilder randomSearchFromBuilder() throws IOException { private static SearchAfterBuilder randomSearchAfterBuilder() throws IOException {
int numSearchFrom = randomIntBetween(1, 10); int numSearchFrom = randomIntBetween(1, 10);
SearchAfterBuilder searchAfterBuilder = new SearchAfterBuilder(); SearchAfterBuilder searchAfterBuilder = new SearchAfterBuilder();
Object[] values = new Object[numSearchFrom]; Object[] values = new Object[numSearchFrom];
@ -109,7 +108,7 @@ public class SearchAfterBuilderTests extends ESTestCase {
// ensure that every number type remain the same before/after xcontent (de)serialization. // ensure that every number type remain the same before/after xcontent (de)serialization.
// This is not a problem because the final type of each field value is extracted from associated sort field. // This is not a problem because the final type of each field value is extracted from associated sort field.
// This little trick ensure that equals and hashcode are the same when using the xcontent serialization. // This little trick ensure that equals and hashcode are the same when using the xcontent serialization.
private SearchAfterBuilder randomJsonSearchFromBuilder() throws IOException { private static SearchAfterBuilder randomJsonSearchFromBuilder() throws IOException {
int numSearchAfter = randomIntBetween(1, 10); int numSearchAfter = randomIntBetween(1, 10);
XContentBuilder jsonBuilder = XContentFactory.jsonBuilder(); XContentBuilder jsonBuilder = XContentFactory.jsonBuilder();
jsonBuilder.startObject(); jsonBuilder.startObject();
@ -159,17 +158,12 @@ public class SearchAfterBuilderTests extends ESTestCase {
} }
private static SearchAfterBuilder serializedCopy(SearchAfterBuilder original) throws IOException { private static SearchAfterBuilder serializedCopy(SearchAfterBuilder original) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) { return copyWriteable(original, new NamedWriteableRegistry(Collections.emptyList()), SearchAfterBuilder::new);
original.writeTo(output);
try (StreamInput in = output.bytes().streamInput()) {
return new SearchAfterBuilder(in);
}
}
} }
public void testSerialization() throws Exception { public void testSerialization() throws Exception {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
SearchAfterBuilder original = randomSearchFromBuilder(); SearchAfterBuilder original = randomSearchAfterBuilder();
SearchAfterBuilder deserialized = serializedCopy(original); SearchAfterBuilder deserialized = serializedCopy(original);
assertEquals(deserialized, original); assertEquals(deserialized, original);
assertEquals(deserialized.hashCode(), original.hashCode()); assertEquals(deserialized.hashCode(), original.hashCode());
@ -179,30 +173,8 @@ public class SearchAfterBuilderTests extends ESTestCase {
public void testEqualsAndHashcode() throws Exception { public void testEqualsAndHashcode() throws Exception {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
SearchAfterBuilder firstBuilder = randomSearchFromBuilder(); // TODO add equals tests with mutating the original object
assertFalse("searchFrom is equal to null", firstBuilder.equals(null)); checkEqualsAndHashCode(randomSearchAfterBuilder(), SearchAfterBuilderTests::serializedCopy);
assertFalse("searchFrom is equal to incompatible type", firstBuilder.equals(""));
assertTrue("searchFrom is not equal to self", firstBuilder.equals(firstBuilder));
assertThat("same searchFrom's hashcode returns different values if called multiple times", firstBuilder.hashCode(),
equalTo(firstBuilder.hashCode()));
SearchAfterBuilder secondBuilder = serializedCopy(firstBuilder);
assertTrue("searchFrom is not equal to self", secondBuilder.equals(secondBuilder));
assertTrue("searchFrom is not equal to its copy", firstBuilder.equals(secondBuilder));
assertTrue("equals is not symmetric", secondBuilder.equals(firstBuilder));
assertThat("searchFrom copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
equalTo(firstBuilder.hashCode()));
SearchAfterBuilder thirdBuilder = serializedCopy(secondBuilder);
assertTrue("searchFrom is not equal to self", thirdBuilder.equals(thirdBuilder));
assertTrue("searchFrom is not equal to its copy", secondBuilder.equals(thirdBuilder));
assertThat("searchFrom copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
equalTo(thirdBuilder.hashCode()));
assertTrue("equals is not transitive", firstBuilder.equals(thirdBuilder));
assertThat("searchFrom copy's hashcode is different from original hashcode", firstBuilder.hashCode(),
equalTo(thirdBuilder.hashCode()));
assertTrue("searchFrom is not symmetric", thirdBuilder.equals(secondBuilder));
assertTrue("searchFrom is not symmetric", thirdBuilder.equals(firstBuilder));
} }
} }
@ -249,18 +221,18 @@ public class SearchAfterBuilderTests extends ESTestCase {
} }
/** /**
* Explicitly tests what you can't list as a sortValue. What you can list is tested by {@link #randomSearchFromBuilder()}. * Explicitly tests what you can't list as a sortValue. What you can list is tested by {@link #randomSearchAfterBuilder()}.
*/ */
public void testBadTypes() throws IOException { public void testBadTypes() throws IOException {
randomSearchFromBuilderWithSortValueThrows(new Object()); randomSearchFromBuilderWithSortValueThrows(new Object());
randomSearchFromBuilderWithSortValueThrows(new GeoPoint(0, 0)); randomSearchFromBuilderWithSortValueThrows(new GeoPoint(0, 0));
randomSearchFromBuilderWithSortValueThrows(randomSearchFromBuilder()); randomSearchFromBuilderWithSortValueThrows(randomSearchAfterBuilder());
randomSearchFromBuilderWithSortValueThrows(this); randomSearchFromBuilderWithSortValueThrows(this);
} }
private void randomSearchFromBuilderWithSortValueThrows(Object containing) throws IOException { private static void randomSearchFromBuilderWithSortValueThrows(Object containing) throws IOException {
// Get a valid one // Get a valid one
SearchAfterBuilder builder = randomSearchFromBuilder(); SearchAfterBuilder builder = randomSearchAfterBuilder();
// Now replace its values with one containing the passed in object // Now replace its values with one containing the passed in object
Object[] values = builder.getSortValues(); Object[] values = builder.getSortValues();
values[between(0, values.length - 1)] = containing; values[between(0, values.length - 1)] = containing;

View File

@ -31,8 +31,7 @@ import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.store.RAMDirectory;
import org.elasticsearch.common.Nullable; import org.elasticsearch.common.Nullable;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.common.xcontent.XContentHelper; import org.elasticsearch.common.xcontent.XContentHelper;
@ -52,12 +51,14 @@ import org.junit.BeforeClass;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicInteger;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
@ -83,7 +84,7 @@ public class SliceBuilderTests extends ESTestCase {
indicesQueriesRegistry = null; indicesQueriesRegistry = null;
} }
private SliceBuilder randomSliceBuilder() throws IOException { private static SliceBuilder randomSliceBuilder() throws IOException {
int max = randomIntBetween(2, MAX_SLICE); int max = randomIntBetween(2, MAX_SLICE);
int id = randomInt(max - 1); int id = randomInt(max - 1);
String field = randomAsciiOfLengthBetween(5, 20); String field = randomAsciiOfLengthBetween(5, 20);
@ -91,11 +92,15 @@ public class SliceBuilderTests extends ESTestCase {
} }
private static SliceBuilder serializedCopy(SliceBuilder original) throws IOException { private static SliceBuilder serializedCopy(SliceBuilder original) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) { return copyWriteable(original, new NamedWriteableRegistry(Collections.emptyList()), SliceBuilder::new);
original.writeTo(output);
try (StreamInput in = output.bytes().streamInput()) {
return new SliceBuilder(in);
} }
private static SliceBuilder mutate(SliceBuilder original) throws IOException {
switch (randomIntBetween(0, 2)) {
case 0: return new SliceBuilder(original.getField() + "_xyz", original.getId(), original.getMax());
case 1: return new SliceBuilder(original.getField(), original.getId() + 1, original.getMax());
case 2:
default: return new SliceBuilder(original.getField(), original.getId(), original.getMax() + 1);
} }
} }
@ -108,29 +113,7 @@ public class SliceBuilderTests extends ESTestCase {
} }
public void testEqualsAndHashcode() throws Exception { public void testEqualsAndHashcode() throws Exception {
SliceBuilder firstBuilder = randomSliceBuilder(); checkEqualsAndHashCode(randomSliceBuilder(), SliceBuilderTests::serializedCopy, SliceBuilderTests::mutate);
assertFalse("sliceBuilder is equal to null", firstBuilder.equals(null));
assertFalse("sliceBuilder is equal to incompatible type", firstBuilder.equals(""));
assertTrue("sliceBuilder is not equal to self", firstBuilder.equals(firstBuilder));
assertThat("same searchFrom's hashcode returns different values if called multiple times",
firstBuilder.hashCode(), equalTo(firstBuilder.hashCode()));
SliceBuilder secondBuilder = serializedCopy(firstBuilder);
assertTrue("sliceBuilder is not equal to self", secondBuilder.equals(secondBuilder));
assertTrue("sliceBuilder is not equal to its copy", firstBuilder.equals(secondBuilder));
assertTrue("equals is not symmetric", secondBuilder.equals(firstBuilder));
assertThat("sliceBuilder copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
equalTo(firstBuilder.hashCode()));
SliceBuilder thirdBuilder = serializedCopy(secondBuilder);
assertTrue("sliceBuilder is not equal to self", thirdBuilder.equals(thirdBuilder));
assertTrue("sliceBuilder is not equal to its copy", secondBuilder.equals(thirdBuilder));
assertThat("sliceBuilder copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
equalTo(thirdBuilder.hashCode()));
assertTrue("equals is not transitive", firstBuilder.equals(thirdBuilder));
assertThat("sliceBuilder copy's hashcode is different from original hashcode", firstBuilder.hashCode(),
equalTo(thirdBuilder.hashCode()));
assertTrue("sliceBuilder is not symmetric", thirdBuilder.equals(secondBuilder));
assertTrue("sliceBuilder is not symmetric", thirdBuilder.equals(firstBuilder));
} }
public void testFromXContent() throws Exception { public void testFromXContent() throws Exception {

View File

@ -24,10 +24,7 @@ import org.apache.lucene.util.Accountable;
import org.elasticsearch.Version; import org.elasticsearch.Version;
import org.elasticsearch.cluster.metadata.IndexMetaData; import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
@ -77,8 +74,7 @@ import java.util.Collections;
import java.util.Map; import java.util.Map;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.hamcrest.Matchers.equalTo; import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.hamcrest.Matchers.not;
public abstract class AbstractSortTestCase<T extends SortBuilder<T>> extends ESTestCase { public abstract class AbstractSortTestCase<T extends SortBuilder<T>> extends ESTestCase {
@ -179,7 +175,7 @@ public abstract class AbstractSortTestCase<T extends SortBuilder<T>> extends EST
public void testSerialization() throws IOException { public void testSerialization() throws IOException {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
T testsort = createTestItem(); T testsort = createTestItem();
T deserializedsort = copyItem(testsort); T deserializedsort = copy(testsort);
assertEquals(testsort, deserializedsort); assertEquals(testsort, deserializedsort);
assertEquals(testsort.hashCode(), deserializedsort.hashCode()); assertEquals(testsort.hashCode(), deserializedsort.hashCode());
assertNotSame(testsort, deserializedsort); assertNotSame(testsort, deserializedsort);
@ -191,29 +187,7 @@ public abstract class AbstractSortTestCase<T extends SortBuilder<T>> extends EST
*/ */
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
T firstsort = createTestItem(); checkEqualsAndHashCode(createTestItem(), this::copy, this::mutate);
assertFalse("sort is equal to null", firstsort.equals(null));
assertFalse("sort is equal to incompatible type", firstsort.equals(""));
assertTrue("sort is not equal to self", firstsort.equals(firstsort));
assertThat("same sort's hashcode returns different values if called multiple times", firstsort.hashCode(),
equalTo(firstsort.hashCode()));
assertThat("different sorts should not be equal", mutate(firstsort), not(equalTo(firstsort)));
assertThat("different sorts should have different hashcode", mutate(firstsort).hashCode(), not(equalTo(firstsort.hashCode())));
T secondsort = copyItem(firstsort);
assertTrue("sort is not equal to self", secondsort.equals(secondsort));
assertTrue("sort is not equal to its copy", firstsort.equals(secondsort));
assertTrue("equals is not symmetric", secondsort.equals(firstsort));
assertThat("sort copy's hashcode is different from original hashcode", secondsort.hashCode(), equalTo(firstsort.hashCode()));
T thirdsort = copyItem(secondsort);
assertTrue("sort is not equal to self", thirdsort.equals(thirdsort));
assertTrue("sort is not equal to its copy", secondsort.equals(thirdsort));
assertThat("sort copy's hashcode is different from original hashcode", secondsort.hashCode(), equalTo(thirdsort.hashCode()));
assertTrue("equals is not transitive", firstsort.equals(thirdsort));
assertThat("sort copy's hashcode is different from original hashcode", firstsort.hashCode(), equalTo(thirdsort.hashCode()));
assertTrue("equals is not symmetric", thirdsort.equals(secondsort));
assertTrue("equals is not symmetric", thirdsort.equals(firstsort));
} }
} }
@ -273,13 +247,8 @@ public abstract class AbstractSortTestCase<T extends SortBuilder<T>> extends EST
} }
} }
@SuppressWarnings("unchecked") private T copy(T original) throws IOException {
private T copyItem(T original) throws IOException { return copyWriteable(original, namedWriteableRegistry,
try (BytesStreamOutput output = new BytesStreamOutput()) { namedWriteableRegistry.getReader(SortBuilder.class, original.getWriteableName()));
original.writeTo(output);
try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), namedWriteableRegistry)) {
return (T) namedWriteableRegistry.getReader(SortBuilder.class, original.getWriteableName()).read(in);
}
}
} }
} }

View File

@ -20,10 +20,7 @@
package org.elasticsearch.search.suggest; package org.elasticsearch.search.suggest;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
@ -41,8 +38,7 @@ import org.junit.BeforeClass;
import java.io.IOException; import java.io.IOException;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.hamcrest.Matchers.equalTo; import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.hamcrest.Matchers.not;
public abstract class AbstractSuggestionBuilderTestCase<SB extends SuggestionBuilder<SB>> extends ESTestCase { public abstract class AbstractSuggestionBuilderTestCase<SB extends SuggestionBuilder<SB>> extends ESTestCase {
@ -77,7 +73,7 @@ public abstract class AbstractSuggestionBuilderTestCase<SB extends SuggestionBui
public void testSerialization() throws IOException { public void testSerialization() throws IOException {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
SB original = randomTestBuilder(); SB original = randomTestBuilder();
SB deserialized = serializedCopy(original); SB deserialized = copy(original);
assertEquals(deserialized, original); assertEquals(deserialized, original);
assertEquals(deserialized.hashCode(), original.hashCode()); assertEquals(deserialized.hashCode(), original.hashCode());
assertNotSame(deserialized, original); assertNotSame(deserialized, original);
@ -111,32 +107,7 @@ public abstract class AbstractSuggestionBuilderTestCase<SB extends SuggestionBui
*/ */
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTBUILDERS; runs++) {
SB firstBuilder = randomTestBuilder(); checkEqualsAndHashCode(randomTestBuilder(), this::copy, this::mutate);
assertFalse("suggestion builder is equal to null", firstBuilder.equals(null));
assertFalse("suggestion builder is equal to incompatible type", firstBuilder.equals(""));
assertTrue("suggestion builder is not equal to self", firstBuilder.equals(firstBuilder));
assertThat("same suggestion builder's hashcode returns different values if called multiple times", firstBuilder.hashCode(),
equalTo(firstBuilder.hashCode()));
final SB mutate = mutate(firstBuilder);
assertThat("different suggestion builders should not be equal", mutate, not(equalTo(firstBuilder)));
SB secondBuilder = serializedCopy(firstBuilder);
assertTrue("suggestion builder is not equal to self", secondBuilder.equals(secondBuilder));
assertTrue("suggestion builder is not equal to its copy", firstBuilder.equals(secondBuilder));
assertTrue("equals is not symmetric", secondBuilder.equals(firstBuilder));
assertThat("suggestion builder copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
equalTo(firstBuilder.hashCode()));
SB thirdBuilder = serializedCopy(secondBuilder);
assertTrue("suggestion builder is not equal to self", thirdBuilder.equals(thirdBuilder));
assertTrue("suggestion builder is not equal to its copy", secondBuilder.equals(thirdBuilder));
assertThat("suggestion builder copy's hashcode is different from original hashcode", secondBuilder.hashCode(),
equalTo(thirdBuilder.hashCode()));
assertTrue("equals is not transitive", firstBuilder.equals(thirdBuilder));
assertThat("suggestion builder copy's hashcode is different from original hashcode", firstBuilder.hashCode(),
equalTo(thirdBuilder.hashCode()));
assertTrue("equals is not symmetric", thirdBuilder.equals(secondBuilder));
assertTrue("equals is not symmetric", thirdBuilder.equals(firstBuilder));
} }
} }
@ -177,7 +148,7 @@ public abstract class AbstractSuggestionBuilderTestCase<SB extends SuggestionBui
} }
private SB mutate(SB firstBuilder) throws IOException { private SB mutate(SB firstBuilder) throws IOException {
SB mutation = serializedCopy(firstBuilder); SB mutation = copy(firstBuilder);
assertNotSame(mutation, firstBuilder); assertNotSame(mutation, firstBuilder);
// change ither one of the shared SuggestionBuilder parameters, or delegate to the specific tests mutate method // change ither one of the shared SuggestionBuilder parameters, or delegate to the specific tests mutate method
if (randomBoolean()) { if (randomBoolean()) {
@ -213,14 +184,9 @@ public abstract class AbstractSuggestionBuilderTestCase<SB extends SuggestionBui
*/ */
protected abstract void mutateSpecificParameters(SB firstBuilder) throws IOException; protected abstract void mutateSpecificParameters(SB firstBuilder) throws IOException;
@SuppressWarnings("unchecked") protected SB copy(SB original) throws IOException {
protected SB serializedCopy(SB original) throws IOException { return copyWriteable(original, namedWriteableRegistry,
try (BytesStreamOutput output = new BytesStreamOutput()) { namedWriteableRegistry.getReader(SuggestionBuilder.class, original.getWriteableName()));
output.writeNamedWriteable(original);
try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), namedWriteableRegistry)) {
return (SB) in.readNamedWriteable(SuggestionBuilder.class);
}
}
} }
protected static QueryParseContext newParseContext(final String xcontent) throws IOException { protected static QueryParseContext newParseContext(final String xcontent) throws IOException {

View File

@ -21,7 +21,6 @@ package org.elasticsearch.search.suggest;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
@ -33,9 +32,9 @@ import org.elasticsearch.index.query.QueryParseContext;
import org.elasticsearch.indices.query.IndicesQueriesRegistry; import org.elasticsearch.indices.query.IndicesQueriesRegistry;
import org.elasticsearch.search.SearchModule; import org.elasticsearch.search.SearchModule;
import org.elasticsearch.search.suggest.completion.CompletionSuggesterBuilderTests; import org.elasticsearch.search.suggest.completion.CompletionSuggesterBuilderTests;
import org.elasticsearch.search.suggest.completion.WritableTestCase;
import org.elasticsearch.search.suggest.phrase.PhraseSuggestionBuilderTests; import org.elasticsearch.search.suggest.phrase.PhraseSuggestionBuilderTests;
import org.elasticsearch.search.suggest.term.TermSuggestionBuilderTests; import org.elasticsearch.search.suggest.term.TermSuggestionBuilderTests;
import org.elasticsearch.test.ESTestCase;
import org.junit.AfterClass; import org.junit.AfterClass;
import org.junit.BeforeClass; import org.junit.BeforeClass;
@ -43,9 +42,11 @@ import java.io.IOException;
import java.util.Map.Entry; import java.util.Map.Entry;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
public class SuggestBuilderTests extends WritableTestCase<SuggestBuilder> { public class SuggestBuilderTests extends ESTestCase {
private static final int NUMBER_OF_RUNS = 20;
private static NamedWriteableRegistry namedWriteableRegistry; private static NamedWriteableRegistry namedWriteableRegistry;
private static Suggesters suggesters; private static Suggesters suggesters;
@ -65,17 +66,12 @@ public class SuggestBuilderTests extends WritableTestCase<SuggestBuilder> {
suggesters = null; suggesters = null;
} }
@Override
protected NamedWriteableRegistry provideNamedWritableRegistry() {
return namedWriteableRegistry;
}
/** /**
* creates random suggestion builder, renders it to xContent and back to new instance that should be equal to original * creates random suggestion builder, renders it to xContent and back to new instance that should be equal to original
*/ */
public void testFromXContent() throws IOException { public void testFromXContent() throws IOException {
for (int runs = 0; runs < NUMBER_OF_RUNS; runs++) { for (int runs = 0; runs < NUMBER_OF_RUNS; runs++) {
SuggestBuilder suggestBuilder = createTestModel(); SuggestBuilder suggestBuilder = randomSuggestBuilder();
XContentBuilder xContentBuilder = XContentFactory.contentBuilder(randomFrom(XContentType.values())); XContentBuilder xContentBuilder = XContentFactory.contentBuilder(randomFrom(XContentType.values()));
if (randomBoolean()) { if (randomBoolean()) {
xContentBuilder.prettyPrint(); xContentBuilder.prettyPrint();
@ -90,6 +86,30 @@ public class SuggestBuilderTests extends WritableTestCase<SuggestBuilder> {
} }
} }
/**
* Test equality and hashCode properties
*/
public void testEqualsAndHashcode() throws IOException {
for (int runs = 0; runs < NUMBER_OF_RUNS; runs++) {
checkEqualsAndHashCode(randomSuggestBuilder(), original -> {
return copyWriteable(original, namedWriteableRegistry, SuggestBuilder::new);
}, this::createMutation);
}
}
/**
* Test serialization and deserialization
*/
public void testSerialization() throws IOException {
for (int i = 0; i < NUMBER_OF_RUNS; i++) {
SuggestBuilder suggestBuilder = randomSuggestBuilder();
SuggestBuilder deserializedModel = copyWriteable(suggestBuilder, namedWriteableRegistry, SuggestBuilder::new);
assertEquals(suggestBuilder, deserializedModel);
assertEquals(suggestBuilder.hashCode(), deserializedModel.hashCode());
assertNotSame(suggestBuilder, deserializedModel);
}
}
public void testIllegalSuggestionName() { public void testIllegalSuggestionName() {
try { try {
new SuggestBuilder().addSuggestion(null, PhraseSuggestionBuilderTests.randomPhraseSuggestionBuilder()); new SuggestBuilder().addSuggestion(null, PhraseSuggestionBuilderTests.randomPhraseSuggestionBuilder());
@ -107,12 +127,6 @@ public class SuggestBuilderTests extends WritableTestCase<SuggestBuilder> {
} }
} }
@Override
protected SuggestBuilder createTestModel() {
return randomSuggestBuilder();
}
@Override
protected SuggestBuilder createMutation(SuggestBuilder original) throws IOException { protected SuggestBuilder createMutation(SuggestBuilder original) throws IOException {
SuggestBuilder mutation = new SuggestBuilder().setGlobalText(original.getGlobalText()); SuggestBuilder mutation = new SuggestBuilder().setGlobalText(original.getGlobalText());
for (Entry<String, SuggestionBuilder<?>> suggestionBuilder : original.getSuggestions().entrySet()) { for (Entry<String, SuggestionBuilder<?>> suggestionBuilder : original.getSuggestions().entrySet()) {
@ -126,11 +140,6 @@ public class SuggestBuilderTests extends WritableTestCase<SuggestBuilder> {
return mutation; return mutation;
} }
@Override
protected SuggestBuilder readFrom(StreamInput in) throws IOException {
return new SuggestBuilder(in);
}
public static SuggestBuilder randomSuggestBuilder() { public static SuggestBuilder randomSuggestBuilder() {
SuggestBuilder builder = new SuggestBuilder(); SuggestBuilder builder = new SuggestBuilder();
if (randomBoolean()) { if (randomBoolean()) {

View File

@ -19,12 +19,20 @@
package org.elasticsearch.search.suggest.completion; package org.elasticsearch.search.suggest.completion;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.unit.Fuzziness; import org.elasticsearch.common.unit.Fuzziness;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class FuzzyOptionsTests extends WritableTestCase<FuzzyOptions> { import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
public class FuzzyOptionsTests extends ESTestCase {
private static final int NUMBER_OF_RUNS = 20;
public static FuzzyOptions randomFuzzyOptions() { public static FuzzyOptions randomFuzzyOptions() {
final FuzzyOptions.Builder builder = FuzzyOptions.builder(); final FuzzyOptions.Builder builder = FuzzyOptions.builder();
@ -41,49 +49,45 @@ public class FuzzyOptionsTests extends WritableTestCase<FuzzyOptions> {
return builder.build(); return builder.build();
} }
@Override
protected FuzzyOptions createTestModel() {
return randomFuzzyOptions();
}
@Override
protected FuzzyOptions createMutation(FuzzyOptions original) throws IOException { protected FuzzyOptions createMutation(FuzzyOptions original) throws IOException {
final FuzzyOptions.Builder builder = FuzzyOptions.builder(); final FuzzyOptions.Builder builder = FuzzyOptions.builder();
builder.setFuzziness(original.getEditDistance()) builder.setFuzziness(original.getEditDistance()).setFuzzyPrefixLength(original.getFuzzyPrefixLength())
.setFuzzyPrefixLength(original.getFuzzyPrefixLength()) .setFuzzyMinLength(original.getFuzzyMinLength()).setMaxDeterminizedStates(original.getMaxDeterminizedStates())
.setFuzzyMinLength(original.getFuzzyMinLength()) .setTranspositions(original.isTranspositions()).setUnicodeAware(original.isUnicodeAware());
.setMaxDeterminizedStates(original.getMaxDeterminizedStates()) List<Runnable> mutators = new ArrayList<>();
.setTranspositions(original.isTranspositions()) mutators.add(() -> builder.setFuzziness(randomValueOtherThan(original.getEditDistance(), () -> randomFrom(0, 1, 2))));
.setUnicodeAware(original.isUnicodeAware());
switch (randomIntBetween(0, 5)) { mutators.add(
case 0: () -> builder.setFuzzyPrefixLength(randomValueOtherThan(original.getFuzzyPrefixLength(), () -> randomIntBetween(1, 3))));
builder.setFuzziness(randomValueOtherThan(original.getEditDistance(), () -> randomFrom(0, 1, 2))); mutators.add(() -> builder.setFuzzyMinLength(randomValueOtherThan(original.getFuzzyMinLength(), () -> randomIntBetween(1, 3))));
break; mutators.add(() -> builder
case 1: .setMaxDeterminizedStates(randomValueOtherThan(original.getMaxDeterminizedStates(), () -> randomIntBetween(1, 10))));
builder.setFuzzyPrefixLength(randomValueOtherThan(original.getFuzzyPrefixLength(), () -> mutators.add(() -> builder.setTranspositions(!original.isTranspositions()));
randomIntBetween(1, 3))); mutators.add(() -> builder.setUnicodeAware(!original.isUnicodeAware()));
break; randomFrom(mutators).run();
case 2:
builder.setFuzzyMinLength(randomValueOtherThan(original.getFuzzyMinLength(), () ->
randomIntBetween(1, 3)));
break;
case 3:
builder.setMaxDeterminizedStates(randomValueOtherThan(original.getMaxDeterminizedStates(), () ->
randomIntBetween(1, 10)));
break;
case 4:
builder.setTranspositions(!original.isTranspositions());
break;
case 5:
builder.setUnicodeAware(!original.isUnicodeAware());
break;
}
return builder.build(); return builder.build();
} }
@Override /**
protected FuzzyOptions readFrom(StreamInput in) throws IOException { * Test serialization and deserialization
return new FuzzyOptions(in); */
public void testSerialization() throws IOException {
for (int i = 0; i < NUMBER_OF_RUNS; i++) {
FuzzyOptions testModel = randomFuzzyOptions();
FuzzyOptions deserializedModel = copyWriteable(testModel, new NamedWriteableRegistry(Collections.emptyList()),
FuzzyOptions::new);
assertEquals(testModel, deserializedModel);
assertEquals(testModel.hashCode(), deserializedModel.hashCode());
assertNotSame(testModel, deserializedModel);
}
}
public void testEqualsAndHashCode() throws IOException {
for (int i = 0; i < NUMBER_OF_RUNS; i++) {
checkEqualsAndHashCode(randomFuzzyOptions(),
original -> copyWriteable(original, new NamedWriteableRegistry(Collections.emptyList()), FuzzyOptions::new),
this::createMutation);
}
} }
public void testIllegalArguments() { public void testIllegalArguments() {

View File

@ -19,12 +19,16 @@
package org.elasticsearch.search.suggest.completion; package org.elasticsearch.search.suggest.completion;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.index.query.RegexpFlag; import org.elasticsearch.index.query.RegexpFlag;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException; import java.io.IOException;
import java.util.Collections;
public class RegexOptionsTests extends WritableTestCase<RegexOptions> { public class RegexOptionsTests extends ESTestCase {
private static final int NUMBER_OF_RUNS = 20;
public static RegexOptions randomRegexOptions() { public static RegexOptions randomRegexOptions() {
final RegexOptions.Builder builder = RegexOptions.builder(); final RegexOptions.Builder builder = RegexOptions.builder();
@ -42,21 +46,24 @@ public class RegexOptionsTests extends WritableTestCase<RegexOptions> {
return builder.build(); return builder.build();
} }
@Override
protected RegexOptions createTestModel() {
return randomRegexOptions();
}
@Override
protected RegexOptions createMutation(RegexOptions original) throws IOException { protected RegexOptions createMutation(RegexOptions original) throws IOException {
final RegexOptions.Builder builder = RegexOptions.builder(); final RegexOptions.Builder builder = RegexOptions.builder();
builder.setMaxDeterminizedStates(randomValueOtherThan(original.getMaxDeterminizedStates(), () -> randomIntBetween(1, 10))); builder.setMaxDeterminizedStates(randomValueOtherThan(original.getMaxDeterminizedStates(), () -> randomIntBetween(1, 10)));
return builder.build(); return builder.build();
} }
@Override /**
protected RegexOptions readFrom(StreamInput in) throws IOException { * Test serialization and deserialization
return new RegexOptions(in); */
public void testSerialization() throws IOException {
for (int i = 0; i < NUMBER_OF_RUNS; i++) {
RegexOptions testOptions = randomRegexOptions();
RegexOptions deserializedModel = copyWriteable(testOptions, new NamedWriteableRegistry(Collections.emptyList()),
RegexOptions::new);
assertEquals(testOptions, deserializedModel);
assertEquals(testOptions.hashCode(), deserializedModel.hashCode());
assertNotSame(testOptions, deserializedModel);
}
} }
public void testIllegalArgument() { public void testIllegalArgument() {

View File

@ -1,116 +0,0 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.search.suggest.completion;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.test.ESTestCase;
import java.io.IOException;
import java.util.Collections;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
/**
* Base class for testing serialization and equality for
* {@link Writeable} models
*/
public abstract class WritableTestCase<M extends Writeable> extends ESTestCase {
protected static final int NUMBER_OF_RUNS = 20;
/**
* create random model that is put under test
*/
protected abstract M createTestModel();
/**
* mutate the given model so the returned model is different
*/
protected abstract M createMutation(M original) throws IOException;
/**
* Read from a stream.
*/
protected abstract M readFrom(StreamInput in) throws IOException;
/**
* Test serialization and deserialization of the tested model.
*/
public void testSerialization() throws IOException {
for (int i = 0; i < NUMBER_OF_RUNS; i++) {
M testModel = createTestModel();
M deserializedModel = copyModel(testModel);
assertEquals(testModel, deserializedModel);
assertEquals(testModel.hashCode(), deserializedModel.hashCode());
assertNotSame(testModel, deserializedModel);
}
}
/**
* Test equality and hashCode properties
*/
@SuppressWarnings("unchecked")
public void testEqualsAndHashcode() throws IOException {
M firstModel = createTestModel();
String modelName = firstModel.getClass().getSimpleName();
assertFalse(modelName + " is equal to null", firstModel.equals(null));
assertFalse(modelName + " is equal to incompatible type", firstModel.equals(""));
assertTrue(modelName + " is not equal to self", firstModel.equals(firstModel));
assertThat("same "+ modelName + "'s hashcode returns different values if called multiple times", firstModel.hashCode(),
equalTo(firstModel.hashCode()));
assertThat("different " + modelName + " should not be equal", createMutation(firstModel), not(equalTo(firstModel)));
M secondModel = copyModel(firstModel);
assertTrue(modelName + " is not equal to self", secondModel.equals(secondModel));
assertTrue(modelName + " is not equal to its copy", firstModel.equals(secondModel));
assertTrue("equals is not symmetric", secondModel.equals(firstModel));
assertThat(modelName + " copy's hashcode is different from original hashcode", secondModel.hashCode(),
equalTo(firstModel.hashCode()));
M thirdModel = copyModel(secondModel);
assertTrue(modelName + " is not equal to self", thirdModel.equals(thirdModel));
assertTrue(modelName + " is not equal to its copy", secondModel.equals(thirdModel));
assertThat(modelName + " copy's hashcode is different from original hashcode", secondModel.hashCode(),
equalTo(thirdModel.hashCode()));
assertTrue("equals is not transitive", firstModel.equals(thirdModel));
assertThat(modelName + " copy's hashcode is different from original hashcode", firstModel.hashCode(),
equalTo(thirdModel.hashCode()));
assertTrue(modelName + " equals is not symmetric", thirdModel.equals(secondModel));
assertTrue(modelName + " equals is not symmetric", thirdModel.equals(firstModel));
}
private M copyModel(M original) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) {
original.writeTo(output);
try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), provideNamedWritableRegistry())) {
return readFrom(in);
}
}
}
protected NamedWriteableRegistry provideNamedWritableRegistry() {
return new NamedWriteableRegistry(Collections.emptyList());
}
}

View File

@ -21,8 +21,7 @@ package org.elasticsearch.search.suggest.phrase;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.ParsingException; import org.elasticsearch.common.ParsingException;
import org.elasticsearch.common.io.stream.BytesStreamOutput; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory; import org.elasticsearch.common.xcontent.XContentFactory;
@ -35,8 +34,12 @@ import org.elasticsearch.search.suggest.phrase.PhraseSuggestionContext.DirectCan
import org.elasticsearch.test.ESTestCase; import org.elasticsearch.test.ESTestCase;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Supplier;
import static org.hamcrest.Matchers.equalTo; import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
public class DirectCandidateGeneratorTests extends ESTestCase{ public class DirectCandidateGeneratorTests extends ESTestCase{
@ -51,7 +54,7 @@ public class DirectCandidateGeneratorTests extends ESTestCase{
public void testSerialization() throws IOException { public void testSerialization() throws IOException {
for (int runs = 0; runs < NUMBER_OF_RUNS; runs++) { for (int runs = 0; runs < NUMBER_OF_RUNS; runs++) {
DirectCandidateGeneratorBuilder original = randomCandidateGenerator(); DirectCandidateGeneratorBuilder original = randomCandidateGenerator();
DirectCandidateGeneratorBuilder deserialized = serializedCopy(original); DirectCandidateGeneratorBuilder deserialized = copy(original);
assertEquals(deserialized, original); assertEquals(deserialized, original);
assertEquals(deserialized.hashCode(), original.hashCode()); assertEquals(deserialized.hashCode(), original.hashCode());
assertNotSame(deserialized, original); assertNotSame(deserialized, original);
@ -63,49 +66,40 @@ public class DirectCandidateGeneratorTests extends ESTestCase{
*/ */
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
for (int runs = 0; runs < NUMBER_OF_RUNS; runs++) { for (int runs = 0; runs < NUMBER_OF_RUNS; runs++) {
DirectCandidateGeneratorBuilder first = randomCandidateGenerator(); final DirectCandidateGeneratorBuilder original = randomCandidateGenerator();
assertFalse("generator is equal to null", first.equals(null)); checkEqualsAndHashCode(original, DirectCandidateGeneratorTests::copy, DirectCandidateGeneratorTests::mutate);
assertFalse("generator is equal to incompatible type", first.equals(""));
assertTrue("generator is not equal to self", first.equals(first));
assertThat("same generator's hashcode returns different values if called multiple times", first.hashCode(),
equalTo(first.hashCode()));
DirectCandidateGeneratorBuilder second = serializedCopy(first);
assertTrue("generator is not equal to self", second.equals(second));
assertTrue("generator is not equal to its copy", first.equals(second));
assertTrue("equals is not symmetric", second.equals(first));
assertThat("generator copy's hashcode is different from original hashcode", second.hashCode(), equalTo(first.hashCode()));
DirectCandidateGeneratorBuilder third = serializedCopy(second);
assertTrue("generator is not equal to self", third.equals(third));
assertTrue("generator is not equal to its copy", second.equals(third));
assertThat("generator copy's hashcode is different from original hashcode", second.hashCode(), equalTo(third.hashCode()));
assertTrue("equals is not transitive", first.equals(third));
assertThat("generator copy's hashcode is different from original hashcode", first.hashCode(), equalTo(third.hashCode()));
assertTrue("equals is not symmetric", third.equals(second));
assertTrue("equals is not symmetric", third.equals(first));
// test for non-equality, check that all fields are covered by changing one by one
first = new DirectCandidateGeneratorBuilder("aaa");
assertEquals(first, serializedCopy(first));
second = new DirectCandidateGeneratorBuilder("bbb");
assertNotEquals(first, second);
assertNotEquals(first.accuracy(0.1f), serializedCopy(first).accuracy(0.2f));
assertNotEquals(first.maxEdits(1), serializedCopy(first).maxEdits(2));
assertNotEquals(first.maxInspections(1), serializedCopy(first).maxInspections(2));
assertNotEquals(first.maxTermFreq(0.1f), serializedCopy(first).maxTermFreq(0.2f));
assertNotEquals(first.minDocFreq(0.1f), serializedCopy(first).minDocFreq(0.2f));
assertNotEquals(first.minWordLength(1), serializedCopy(first).minWordLength(2));
assertNotEquals(first.postFilter("postFilter"), serializedCopy(first).postFilter("postFilter_other"));
assertNotEquals(first.preFilter("preFilter"), serializedCopy(first).preFilter("preFilter_other"));
assertNotEquals(first.prefixLength(1), serializedCopy(first).prefixLength(2));
assertNotEquals(first.size(1), serializedCopy(first).size(2));
assertNotEquals(first.sort("score"), serializedCopy(first).sort("frequency"));
assertNotEquals(first.stringDistance("levenstein"), serializedCopy(first).sort("ngram"));
assertNotEquals(first.suggestMode("missing"), serializedCopy(first).suggestMode("always"));
} }
} }
private static DirectCandidateGeneratorBuilder mutate(DirectCandidateGeneratorBuilder original) throws IOException {
DirectCandidateGeneratorBuilder mutation = copy(original);
List<Supplier<DirectCandidateGeneratorBuilder>> mutators = new ArrayList<>();
mutators.add(() -> new DirectCandidateGeneratorBuilder(original.field() + "_other"));
mutators.add(() -> mutation.accuracy(original.accuracy() == null ? 0.1f : original.accuracy() + 0.1f));
mutators.add(() -> {
Integer maxEdits = original.maxEdits() == null ? 1 : original.maxEdits();
if (maxEdits == 1) {
maxEdits = 2;
} else {
maxEdits = 1;
}
return mutation.maxEdits(maxEdits);
});
mutators.add(() -> mutation.maxInspections(original.maxInspections() == null ? 1 : original.maxInspections() + 1));
mutators.add(() -> mutation.minWordLength(original.minWordLength() == null ? 1 : original.minWordLength() + 1));
mutators.add(() -> mutation.prefixLength(original.prefixLength() == null ? 1 : original.prefixLength() + 1));
mutators.add(() -> mutation.size(original.size() == null ? 1 : original.size() + 1));
mutators.add(() -> mutation.maxTermFreq(original.maxTermFreq() == null ? 0.1f : original.maxTermFreq() + 0.1f));
mutators.add(() -> mutation.minDocFreq(original.minDocFreq() == null ? 0.1f : original.minDocFreq() + 0.1f));
mutators.add(() -> mutation.postFilter(original.postFilter() == null ? "postFilter" : original.postFilter() + "_other"));
mutators.add(() -> mutation.preFilter(original.preFilter() == null ? "preFilter" : original.preFilter() + "_other"));
mutators.add(() -> mutation.sort(original.sort() == null ? "score" : original.sort() + "_other"));
mutators.add(
() -> mutation.stringDistance(original.stringDistance() == null ? "levenstein" : original.stringDistance() + "_other"));
mutators.add(() -> mutation.suggestMode(original.suggestMode() == null ? "missing" : original.suggestMode() + "_other"));
return randomFrom(mutators).get();
}
/** /**
* creates random candidate generator, renders it to xContent and back to new instance that should be equal to original * creates random candidate generator, renders it to xContent and back to new instance that should be equal to original
*/ */
@ -204,12 +198,7 @@ public class DirectCandidateGeneratorTests extends ESTestCase{
return generator; return generator;
} }
private static DirectCandidateGeneratorBuilder serializedCopy(DirectCandidateGeneratorBuilder original) throws IOException { private static DirectCandidateGeneratorBuilder copy(DirectCandidateGeneratorBuilder original) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) { return copyWriteable(original, new NamedWriteableRegistry(Collections.emptyList()), DirectCandidateGeneratorBuilder::new);
original.writeTo(output);
try (StreamInput in = output.bytes().streamInput()) {
return new DirectCandidateGeneratorBuilder(in);
}
}
} }
} }

View File

@ -31,10 +31,7 @@ import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.MultiFields; import org.apache.lucene.index.MultiFields;
import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.store.RAMDirectory;
import org.elasticsearch.common.ParseFieldMatcher; import org.elasticsearch.common.ParseFieldMatcher;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.lucene.BytesRefs; import org.elasticsearch.common.lucene.BytesRefs;
import org.elasticsearch.common.xcontent.ToXContent; import org.elasticsearch.common.xcontent.ToXContent;
import org.elasticsearch.common.xcontent.XContentBuilder; import org.elasticsearch.common.xcontent.XContentBuilder;
@ -55,8 +52,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import static org.hamcrest.Matchers.equalTo; import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.hamcrest.Matchers.not;
public abstract class SmoothingModelTestCase extends ESTestCase { public abstract class SmoothingModelTestCase extends ESTestCase {
@ -117,7 +113,6 @@ public abstract class SmoothingModelTestCase extends ESTestCase {
*/ */
public void testBuildWordScorer() throws IOException { public void testBuildWordScorer() throws IOException {
SmoothingModel testModel = createTestModel(); SmoothingModel testModel = createTestModel();
Map<String, Analyzer> mapping = new HashMap<>(); Map<String, Analyzer> mapping = new HashMap<>();
mapping.put("field", new WhitespaceAnalyzer()); mapping.put("field", new WhitespaceAnalyzer());
PerFieldAnalyzerWrapper wrapper = new PerFieldAnalyzerWrapper(new WhitespaceAnalyzer(), mapping); PerFieldAnalyzerWrapper wrapper = new PerFieldAnalyzerWrapper(new WhitespaceAnalyzer(), mapping);
@ -142,7 +137,7 @@ public abstract class SmoothingModelTestCase extends ESTestCase {
*/ */
public void testSerialization() throws IOException { public void testSerialization() throws IOException {
SmoothingModel testModel = createTestModel(); SmoothingModel testModel = createTestModel();
SmoothingModel deserializedModel = copyModel(testModel); SmoothingModel deserializedModel = copy(testModel);
assertEquals(testModel, deserializedModel); assertEquals(testModel, deserializedModel);
assertEquals(testModel.hashCode(), deserializedModel.hashCode()); assertEquals(testModel.hashCode(), deserializedModel.hashCode());
assertNotSame(testModel, deserializedModel); assertNotSame(testModel, deserializedModel);
@ -151,42 +146,12 @@ public abstract class SmoothingModelTestCase extends ESTestCase {
/** /**
* Test equality and hashCode properties * Test equality and hashCode properties
*/ */
@SuppressWarnings("unchecked")
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
SmoothingModel firstModel = createTestModel(); checkEqualsAndHashCode(createTestModel(), this::copy, this::createMutation);
assertFalse("smoothing model is equal to null", firstModel.equals(null));
assertFalse("smoothing model is equal to incompatible type", firstModel.equals(""));
assertTrue("smoothing model is not equal to self", firstModel.equals(firstModel));
assertThat("same smoothing model's hashcode returns different values if called multiple times", firstModel.hashCode(),
equalTo(firstModel.hashCode()));
assertThat("different smoothing models should not be equal", createMutation(firstModel), not(equalTo(firstModel)));
SmoothingModel secondModel = copyModel(firstModel);
assertTrue("smoothing model is not equal to self", secondModel.equals(secondModel));
assertTrue("smoothing model is not equal to its copy", firstModel.equals(secondModel));
assertTrue("equals is not symmetric", secondModel.equals(firstModel));
assertThat("smoothing model copy's hashcode is different from original hashcode", secondModel.hashCode(),
equalTo(firstModel.hashCode()));
SmoothingModel thirdModel = copyModel(secondModel);
assertTrue("smoothing model is not equal to self", thirdModel.equals(thirdModel));
assertTrue("smoothing model is not equal to its copy", secondModel.equals(thirdModel));
assertThat("smoothing model copy's hashcode is different from original hashcode", secondModel.hashCode(),
equalTo(thirdModel.hashCode()));
assertTrue("equals is not transitive", firstModel.equals(thirdModel));
assertThat("smoothing model copy's hashcode is different from original hashcode", firstModel.hashCode(),
equalTo(thirdModel.hashCode()));
assertTrue("equals is not symmetric", thirdModel.equals(secondModel));
assertTrue("equals is not symmetric", thirdModel.equals(firstModel));
} }
static SmoothingModel copyModel(SmoothingModel original) throws IOException { private SmoothingModel copy(SmoothingModel original) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) { return ESTestCase.copyWriteable(original, namedWriteableRegistry,
original.writeTo(output); namedWriteableRegistry.getReader(SmoothingModel.class, original.getWriteableName()));
try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), namedWriteableRegistry)) {
return namedWriteableRegistry.getReader(SmoothingModel.class, original.getWriteableName()).read(in);
} }
} }
}
}

View File

@ -20,6 +20,7 @@
package org.elasticsearch.test; package org.elasticsearch.test;
import com.fasterxml.jackson.core.io.JsonStringEncoder; import com.fasterxml.jackson.core.io.JsonStringEncoder;
import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.BoostQuery;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery; import org.apache.lucene.search.TermQuery;
@ -50,6 +51,7 @@ import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput; import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry; import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput; import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.Writeable.Reader;
import org.elasticsearch.common.settings.IndexScopedSettings; import org.elasticsearch.common.settings.IndexScopedSettings;
import org.elasticsearch.common.settings.Setting; import org.elasticsearch.common.settings.Setting;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
@ -120,12 +122,12 @@ import java.util.Set;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import static java.util.Collections.emptyList; import static java.util.Collections.emptyList;
import static org.elasticsearch.test.EqualsHashCodeTestUtils.checkEqualsAndHashCode;
import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.either; import static org.hamcrest.Matchers.either;
import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.greaterThan; import static org.hamcrest.Matchers.greaterThan;
import static org.hamcrest.Matchers.instanceOf; import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.not;
public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>> extends ESTestCase { public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>> extends ESTestCase {
@ -481,7 +483,7 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
} }
} }
private void queryWrappedInArrayTest(String queryName, String validQuery) throws IOException { private static void queryWrappedInArrayTest(String queryName, String validQuery) throws IOException {
int i = validQuery.indexOf("\"" + queryName + "\""); int i = validQuery.indexOf("\"" + queryName + "\"");
assertThat(i, greaterThan(0)); assertThat(i, greaterThan(0));
@ -744,47 +746,28 @@ public abstract class AbstractQueryTestCase<QB extends AbstractQueryBuilder<QB>>
public void testEqualsAndHashcode() throws IOException { public void testEqualsAndHashcode() throws IOException {
for (int runs = 0; runs < NUMBER_OF_TESTQUERIES; runs++) { for (int runs = 0; runs < NUMBER_OF_TESTQUERIES; runs++) {
QB firstQuery = createTestQueryBuilder(); // TODO we only change name and boost, we should extend by any sub-test supplying a "mutate" method that randomly changes one
assertFalse("query is equal to null", firstQuery.equals(null)); // aspect of the object under test
assertFalse("query is equal to incompatible type", firstQuery.equals("")); checkEqualsAndHashCode(createTestQueryBuilder(), this::copyQuery, this::changeNameOrBoost);
assertTrue("query is not equal to self", firstQuery.equals(firstQuery)); }
assertThat("same query's hashcode returns different values if called multiple times", firstQuery.hashCode(), }
equalTo(firstQuery.hashCode()));
QB secondQuery = copyQuery(firstQuery);
assertTrue("query is not equal to self", secondQuery.equals(secondQuery));
assertTrue("query is not equal to its copy", firstQuery.equals(secondQuery));
assertTrue("equals is not symmetric", secondQuery.equals(firstQuery));
assertThat("query copy's hashcode is different from original hashcode", secondQuery.hashCode(), equalTo(firstQuery.hashCode()));
QB thirdQuery = copyQuery(secondQuery);
assertTrue("query is not equal to self", thirdQuery.equals(thirdQuery));
assertTrue("query is not equal to its copy", secondQuery.equals(thirdQuery));
assertThat("query copy's hashcode is different from original hashcode", secondQuery.hashCode(), equalTo(thirdQuery.hashCode()));
assertTrue("equals is not transitive", firstQuery.equals(thirdQuery));
assertThat("query copy's hashcode is different from original hashcode", firstQuery.hashCode(), equalTo(thirdQuery.hashCode()));
assertTrue("equals is not symmetric", thirdQuery.equals(secondQuery));
assertTrue("equals is not symmetric", thirdQuery.equals(firstQuery));
private QB changeNameOrBoost(QB original) throws IOException {
QB secondQuery = copyQuery(original);
if (randomBoolean()) { if (randomBoolean()) {
secondQuery.queryName(secondQuery.queryName() == null ? randomAsciiOfLengthBetween(1, 30) : secondQuery.queryName() secondQuery.queryName(secondQuery.queryName() == null ? randomAsciiOfLengthBetween(1, 30) : secondQuery.queryName()
+ randomAsciiOfLengthBetween(1, 10)); + randomAsciiOfLengthBetween(1, 10));
} else { } else {
secondQuery.boost(firstQuery.boost() + 1f + randomFloat()); secondQuery.boost(original.boost() + 1f + randomFloat());
}
assertThat("different queries should not be equal", secondQuery, not(equalTo(firstQuery)));
} }
return secondQuery;
} }
//we use the streaming infra to create a copy of the query provided as argument //we use the streaming infra to create a copy of the query provided as argument
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
private QB copyQuery(QB query) throws IOException { private QB copyQuery(QB query) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) { Reader<QB> reader = (Reader<QB>) serviceHolder.namedWriteableRegistry.getReader(QueryBuilder.class, query.getWriteableName());
output.writeNamedWriteable(query); return copyWriteable(query, serviceHolder.namedWriteableRegistry, reader);
try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), serviceHolder.namedWriteableRegistry)) {
return (QB) in.readNamedWriteable(QueryBuilder.class);
}
}
} }
/** /**

View File

@ -29,6 +29,7 @@ import com.carrotsearch.randomizedtesting.generators.RandomInts;
import com.carrotsearch.randomizedtesting.generators.RandomPicks; import com.carrotsearch.randomizedtesting.generators.RandomPicks;
import com.carrotsearch.randomizedtesting.generators.RandomStrings; import com.carrotsearch.randomizedtesting.generators.RandomStrings;
import com.carrotsearch.randomizedtesting.rules.TestRuleAdapter; import com.carrotsearch.randomizedtesting.rules.TestRuleAdapter;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import org.apache.lucene.uninverting.UninvertingReader; import org.apache.lucene.uninverting.UninvertingReader;
import org.apache.lucene.util.LuceneTestCase; import org.apache.lucene.util.LuceneTestCase;
@ -43,6 +44,12 @@ import org.elasticsearch.cluster.metadata.IndexMetaData;
import org.elasticsearch.common.bytes.BytesReference; import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.io.PathUtils; import org.elasticsearch.common.io.PathUtils;
import org.elasticsearch.common.io.PathUtilsForTesting; import org.elasticsearch.common.io.PathUtilsForTesting;
import org.elasticsearch.common.io.stream.BytesStreamOutput;
import org.elasticsearch.common.io.stream.NamedWriteable;
import org.elasticsearch.common.io.stream.NamedWriteableAwareStreamInput;
import org.elasticsearch.common.io.stream.NamedWriteableRegistry;
import org.elasticsearch.common.io.stream.StreamInput;
import org.elasticsearch.common.io.stream.Writeable;
import org.elasticsearch.common.logging.Loggers; import org.elasticsearch.common.logging.Loggers;
import org.elasticsearch.common.settings.Settings; import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.TransportAddress; import org.elasticsearch.common.transport.TransportAddress;
@ -749,6 +756,22 @@ public abstract class ESTestCase extends LuceneTestCase {
return targetMap; return targetMap;
} }
/**
* Create a copy of an original {@link Writeable} object by running it through a {@link BytesStreamOutput} and
* reading it in again using a provided {@link Writeable.Reader}. The stream that is wrapped around the {@link StreamInput}
* potentially need to use a {@link NamedWriteableRegistry}, so this needs to be provided too (although it can be
* empty if the object that is streamed doesn't contain any {@link NamedWriteable} objects itself.
*/
public static <T extends Writeable> T copyWriteable(T original, NamedWriteableRegistry namedWritabelRegistry,
Writeable.Reader<T> reader) throws IOException {
try (BytesStreamOutput output = new BytesStreamOutput()) {
original.writeTo(output);
try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), namedWritabelRegistry)) {
return reader.read(in);
}
}
}
/** /**
* Returns true iff assertions for elasticsearch packages are enabled * Returns true iff assertions for elasticsearch packages are enabled
*/ */

View File

@ -0,0 +1,102 @@
/*
* Licensed to Elasticsearch under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.elasticsearch.test;
import java.io.IOException;
import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
/**
* Utility class that encapsulates standard checks and assertions around testing the equals() and hashCode()
* methods of objects that implement them.
*/
public class EqualsHashCodeTestUtils {
private static Object[] someObjects = new Object[] { "some string", new Integer(1), new Double(1.0) };
/**
* A function that makes a copy of its input argument
*/
public interface CopyFunction<T> {
T copy(T t) throws IOException;
};
/**
* A function that creates a copy of its input argument that is different from its
* input in exactly one aspect (e.g. one parameter of a class instance should change)
*/
public interface MutateFunction<T> {
T mutate(T t) throws IOException;
};
/**
* Perform common equality and hashCode checks on the input object
* @param original the object under test
* @param copyFunction a function that creates a deep copy of the input object
*/
public static <T> void checkEqualsAndHashCode(T original, CopyFunction<T> copyFunction) {
checkEqualsAndHashCode(original, copyFunction, null);
}
/**
* Perform common equality and hashCode checks on the input object
* @param original the object under test
* @param copyFunction a function that creates a deep copy of the input object
* @param mutationFunction a function that creates a copy of the input object that is different
* from the input in one aspect. The output of this call is used to check that it is not equal()
* to the input object
*/
public static <T> void checkEqualsAndHashCode(T original, CopyFunction<T> copyFunction,
MutateFunction<T> mutationFunction) {
try {
String objectName = original.getClass().getSimpleName();
assertFalse(objectName + " is equal to null", original.equals(null));
// TODO not sure how useful the following test is
assertFalse(objectName + " is equal to incompatible type", original.equals(ESTestCase.randomFrom(someObjects)));
assertTrue(objectName + " is not equal to self", original.equals(original));
assertThat(objectName + " hashcode returns different values if called multiple times", original.hashCode(),
equalTo(original.hashCode()));
if (mutationFunction != null) {
assertThat(objectName + " mutation should not be equal to original", mutationFunction.mutate(original),
not(equalTo(original)));
}
T copy = copyFunction.copy(original);
assertTrue(objectName + " copy is not equal to self", copy.equals(copy));
assertTrue(objectName + " is not equal to its copy", original.equals(copy));
assertTrue("equals is not symmetric", copy.equals(original));
assertThat(objectName + " hashcode is different from copies hashcode", copy.hashCode(), equalTo(original.hashCode()));
T secondCopy = copyFunction.copy(copy);
assertTrue("second copy is not equal to self", secondCopy.equals(secondCopy));
assertTrue("copy is not equal to its second copy", copy.equals(secondCopy));
assertThat("second copy's hashcode is different from original hashcode", copy.hashCode(), equalTo(secondCopy.hashCode()));
assertTrue("equals is not transitive", original.equals(secondCopy));
assertTrue("equals is not symmetric", secondCopy.equals(copy));
assertTrue("equals is not symmetric", secondCopy.equals(original));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}