/* * 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 org.elasticsearch.Version; 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.Streamable; import java.io.IOException; import java.util.Collections; import static org.hamcrest.Matchers.equalTo; public abstract class AbstractStreamableTestCase<T extends Streamable> extends ESTestCase { protected static final int NUMBER_OF_TEST_RUNS = 20; /** * Creates a random test instance to use in the tests. This method will be * called multiple times during test execution and should return a different * random instance each time it is called. */ protected abstract T createTestInstance(); /** * Creates an empty instance to use when deserialising the * {@link Streamable}. This usually returns an instance created using the * zer-arg constructor */ protected abstract T createBlankInstance(); /** * Tests that the equals and hashcode methods are consistent and copied * versions of the instance have are equal. */ public void testEqualsAndHashcode() throws IOException { for (int runs = 0; runs < NUMBER_OF_TEST_RUNS; runs++) { T firstInstance = createTestInstance(); assertFalse("instance is equal to null", firstInstance.equals(null)); assertFalse("instance is equal to incompatible type", firstInstance.equals("")); assertEquals("instance is not equal to self", firstInstance, firstInstance); assertThat("same instance's hashcode returns different values if called multiple times", firstInstance.hashCode(), equalTo(firstInstance.hashCode())); T secondInstance = copyInstance(firstInstance, Version.CURRENT); assertEquals("instance is not equal to self", secondInstance, secondInstance); assertEquals("instance is not equal to its copy", firstInstance, secondInstance); assertEquals("equals is not symmetric", secondInstance, firstInstance); assertThat("instance copy's hashcode is different from original hashcode", secondInstance.hashCode(), equalTo(firstInstance.hashCode())); T thirdInstance = copyInstance(secondInstance, Version.CURRENT); assertEquals("instance is not equal to self", thirdInstance, thirdInstance); assertEquals("instance is not equal to its copy", secondInstance, thirdInstance); assertThat("instance copy's hashcode is different from original hashcode", secondInstance.hashCode(), equalTo(thirdInstance.hashCode())); assertEquals("equals is not transitive", firstInstance, thirdInstance); assertThat("instance copy's hashcode is different from original hashcode", firstInstance.hashCode(), equalTo(thirdInstance.hashCode())); assertEquals("equals is not symmetric", thirdInstance, secondInstance); assertEquals("equals is not symmetric", thirdInstance, firstInstance); } } /** * Test serialization and deserialization of the test instance. */ public void testSerialization() throws IOException { for (int runs = 0; runs < NUMBER_OF_TEST_RUNS; runs++) { T testInstance = createTestInstance(); assertSerialization(testInstance); } } /** * Serialize the given instance and asserts that both are equal */ protected T assertSerialization(T testInstance) throws IOException { T deserializedInstance = copyInstance(testInstance, Version.CURRENT); assertEquals(testInstance, deserializedInstance); assertEquals(testInstance.hashCode(), deserializedInstance.hashCode()); assertNotSame(testInstance, deserializedInstance); return deserializedInstance; } /** * Round trip {@code instance} through binary serialization, setting the wire compatibility version to {@code version}. */ protected T copyInstance(T instance, Version version) throws IOException { try (BytesStreamOutput output = new BytesStreamOutput()) { output.setVersion(version); instance.writeTo(output); try (StreamInput in = new NamedWriteableAwareStreamInput(output.bytes().streamInput(), getNamedWriteableRegistry())) { in.setVersion(version); T newInstance = createBlankInstance(); newInstance.readFrom(in); return newInstance; } } } /** * Get the {@link NamedWriteableRegistry} to use when de-serializing the object. * * Override this method if you need to register {@link NamedWriteable}s for the test object to de-serialize. * * By default this will return a {@link NamedWriteableRegistry} with no registered {@link NamedWriteable}s */ protected NamedWriteableRegistry getNamedWriteableRegistry() { return new NamedWriteableRegistry(Collections.emptyList()); } }