Core Runtime: Fix Property and Value equality Fix the spotted bug and make equals/hashcode behaviour uniform accross PropertyInstance, AssociationInstance, ManyAssociationInstance and ValueInstance. Transients, Entities and Services remain untouched.
Assertions are in PropertyEqualityTest, AssociationEqualityTest and ValueEqualityTest. Project: http://git-wip-us.apache.org/repos/asf/zest-qi4j/repo Commit: http://git-wip-us.apache.org/repos/asf/zest-qi4j/commit/ff99cbd0 Tree: http://git-wip-us.apache.org/repos/asf/zest-qi4j/tree/ff99cbd0 Diff: http://git-wip-us.apache.org/repos/asf/zest-qi4j/diff/ff99cbd0 Branch: refs/heads/master Commit: ff99cbd05668eab6123455b133af92f584cf4410 Parents: 85c1ca2 Author: Paul Merlin <[email protected]> Authored: Tue Feb 19 15:26:17 2013 +0100 Committer: Paul Merlin <[email protected]> Committed: Tue Feb 19 17:31:48 2013 +0100 ---------------------------------------------------------------------- core/api/src/docs/valuecomposite.txt | 3 +- .../org/qi4j/api/entity/EntityReference.java | 4 +- .../association/AssociationInstance.java | 28 +- .../runtime/association/AssociationModel.java | 4 +- .../association/ManyAssociationInstance.java | 19 +- .../association/ManyAssociationModel.java | 4 +- .../qi4j/runtime/property/PropertyInstance.java | 35 +- .../qi4j/runtime/property/PropertyModel.java | 4 +- .../org/qi4j/runtime/value/ValueInstance.java | 24 +- .../association/AssociationEqualityTest.java | 388 +++++++++++++++++ .../runtime/property/PropertyEqualityTest.java | 430 +++++++++++++++++++ .../qi4j/runtime/value/ValueEqualityTest.java | 238 ++++++++++ 12 files changed, 1145 insertions(+), 36 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/ff99cbd0/core/api/src/docs/valuecomposite.txt ---------------------------------------------------------------------- diff --git a/core/api/src/docs/valuecomposite.txt b/core/api/src/docs/valuecomposite.txt index d5a0e05..dbd2f3c 100644 --- a/core/api/src/docs/valuecomposite.txt +++ b/core/api/src/docs/valuecomposite.txt @@ -28,8 +28,9 @@ of an EntityComposite via a Property. The characteristics of a ValueComposite compared to other Composite meta types are; * It is Immutable. - * Its equals/hashCode works on the values of the ValueComposite. + * Its equals/hashCode works on both the descriptor and the values of the ValueComposite. * Can be used as Property types. + * Can be serialized and deserialized. == Value Serialization == Value objects can be serialized and deserialized using the ValueSerialization API which is a Service API implemented http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/ff99cbd0/core/api/src/main/java/org/qi4j/api/entity/EntityReference.java ---------------------------------------------------------------------- diff --git a/core/api/src/main/java/org/qi4j/api/entity/EntityReference.java b/core/api/src/main/java/org/qi4j/api/entity/EntityReference.java index 5cdece2..60c4d1b 100644 --- a/core/api/src/main/java/org/qi4j/api/entity/EntityReference.java +++ b/core/api/src/main/java/org/qi4j/api/entity/EntityReference.java @@ -112,9 +112,7 @@ public final class EntityReference @Override public int hashCode() { - int result; - result = identity.hashCode(); - return result; + return identity.hashCode(); } /** http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/ff99cbd0/core/runtime/src/main/java/org/qi4j/runtime/association/AssociationInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/association/AssociationInstance.java b/core/runtime/src/main/java/org/qi4j/runtime/association/AssociationInstance.java index 6672536..d94aae2 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/association/AssociationInstance.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/association/AssociationInstance.java @@ -16,6 +16,7 @@ package org.qi4j.runtime.association; import java.lang.reflect.Type; import org.qi4j.api.association.Association; +import org.qi4j.api.association.AssociationDescriptor; import org.qi4j.api.entity.EntityReference; import org.qi4j.api.property.Property; import org.qi4j.functional.Function2; @@ -79,14 +80,12 @@ public final class AssociationInstance<T> @Override public int hashCode() { - if( associationState.get() == null ) + int hash = associationInfo.hashCode() * 61; // Descriptor + if( associationState.get() != null ) { - return 0; - } - else - { - return associationState.get().hashCode(); + hash += associationState.get().hashCode() * 3; // State } + return hash; } @Override @@ -100,15 +99,20 @@ public final class AssociationInstance<T> { return false; } - - AssociationInstance that = (AssociationInstance) o; - - if( associationState.get() != null ? !associationState.get() - .equals( that.associationState.get() ) : that.associationState.get() != null ) + AssociationInstance<?> that = (AssociationInstance) o; + AssociationDescriptor thatDescriptor = (AssociationDescriptor) that.associationInfo(); + // Descriptor equality + if( !associationInfo.equals( thatDescriptor ) ) + { + return false; + } + // State equality + if( associationState.get() != null + ? !associationState.get().equals( that.associationState.get() ) + : that.associationState.get() != null ) { return false; } - return true; } } http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/ff99cbd0/core/runtime/src/main/java/org/qi4j/runtime/association/AssociationModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/association/AssociationModel.java b/core/runtime/src/main/java/org/qi4j/runtime/association/AssociationModel.java index bb128a4..d1d5f36 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/association/AssociationModel.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/association/AssociationModel.java @@ -44,7 +44,9 @@ import static org.qi4j.functional.Iterables.empty; import static org.qi4j.functional.Iterables.first; /** - * JAVADOC + * Model for an Association. + * + * <p>Equality is based on the Association accessor object (associated type and name), not on the QualifiedName.</p> */ public final class AssociationModel implements AssociationDescriptor, AssociationInfo, Binder, Visitable<AssociationModel> http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/ff99cbd0/core/runtime/src/main/java/org/qi4j/runtime/association/ManyAssociationInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/association/ManyAssociationInstance.java b/core/runtime/src/main/java/org/qi4j/runtime/association/ManyAssociationInstance.java index 3bbed32..ad5a734 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/association/ManyAssociationInstance.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/association/ManyAssociationInstance.java @@ -6,6 +6,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Set; +import org.qi4j.api.association.AssociationDescriptor; import org.qi4j.api.association.ManyAssociation; import org.qi4j.api.entity.EntityReference; import org.qi4j.functional.Function2; @@ -120,13 +121,20 @@ public class ManyAssociationInstance<T> return false; } ManyAssociationInstance<?> that = (ManyAssociationInstance) o; + AssociationDescriptor thatDescriptor = (AssociationDescriptor) that.associationInfo(); + // Descriptor equality + if( !associationInfo.equals( thatDescriptor ) ) + { + return false; + } + // State equality if( manyAssociationState.count() != that.manyAssociationState.count() ) { return false; } for( EntityReference ref : manyAssociationState ) { - if(!that.manyAssociationState.contains( ref ) ) + if( !that.manyAssociationState.contains( ref ) ) { return false; } @@ -137,9 +145,12 @@ public class ManyAssociationInstance<T> @Override public int hashCode() { - int result = super.hashCode(); - result = 31 * result + manyAssociationState.hashCode(); - return result; + int hash = associationInfo.hashCode() * 31; // Descriptor + for( EntityReference ref : manyAssociationState ) + { + hash += ref.hashCode() * 7; // State + } + return hash; } public ManyAssociationState getManyAssociationState() http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/ff99cbd0/core/runtime/src/main/java/org/qi4j/runtime/association/ManyAssociationModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/association/ManyAssociationModel.java b/core/runtime/src/main/java/org/qi4j/runtime/association/ManyAssociationModel.java index 1eef31b..69f0f82 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/association/ManyAssociationModel.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/association/ManyAssociationModel.java @@ -48,7 +48,9 @@ import static org.qi4j.functional.Iterables.empty; import static org.qi4j.functional.Iterables.first; /** - * JAVADOC + * Model for a ManyAssociation. + * + * <p>Equality is based on the ManyAssociation accessor object (associated type and name), not on the QualifiedName.</p> */ public final class ManyAssociationModel implements AssociationDescriptor, AssociationInfo, Binder, Visitable<ManyAssociationModel> http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/ff99cbd0/core/runtime/src/main/java/org/qi4j/runtime/property/PropertyInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/property/PropertyInstance.java b/core/runtime/src/main/java/org/qi4j/runtime/property/PropertyInstance.java index b54330e..75bda37 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/property/PropertyInstance.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/property/PropertyInstance.java @@ -23,6 +23,8 @@ import java.util.List; import java.util.Map; import java.util.Set; import org.qi4j.api.property.Property; +import org.qi4j.api.property.PropertyDescriptor; +import org.qi4j.api.property.PropertyWrapper; import org.qi4j.api.type.CollectionType; import org.qi4j.api.type.MapType; import org.qi4j.api.type.ValueCompositeType; @@ -55,6 +57,9 @@ public class PropertyInstance<T> return model; } + /** + * @param model The property model. This argument must not be {@code null}. + */ public void setPropertyInfo( PropertyInfo model ) { this.model = model; @@ -91,12 +96,12 @@ public class PropertyInstance<T> /** * Perform equals with {@code o} argument. - * <p/> - * The definition of equals() for the property is that if the value and subclass are - * equal, then the properties are equal + * <p> + * The definition of equals() for the Property is that if both the state and descriptor are equal, + * then the properties are equal. + * </p> * * @param o The other object to compare. - * * @return Returns a {@code boolean} indicator whether this object is equals the other. */ @Override @@ -112,7 +117,18 @@ public class PropertyInstance<T> } Property<?> that = (Property<?>) o; - + // Unwrap if needed + while( that instanceof PropertyWrapper ) + { + that = ( (PropertyWrapper) that ).next(); + } + PropertyDescriptor thatDescriptor = (PropertyDescriptor) ( (PropertyInstance) that ).propertyInfo(); + // Descriptor equality + if( !model.equals( thatDescriptor ) ) + { + return false; + } + // State equality T value = get(); if( value == null ) { @@ -129,16 +145,11 @@ public class PropertyInstance<T> @Override public int hashCode() { - int hash = getClass().hashCode(); - if( model != null ) - { - hash = model.type().hashCode(); - } - hash = hash * 19; + int hash = model.hashCode() * 19; // Descriptor T value = get(); if( value != null ) { - hash = hash + value.hashCode() * 13; + hash += value.hashCode() * 13; // State } return hash; } http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/ff99cbd0/core/runtime/src/main/java/org/qi4j/runtime/property/PropertyModel.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/property/PropertyModel.java b/core/runtime/src/main/java/org/qi4j/runtime/property/PropertyModel.java index 05eb2f9..e547d8a 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/property/PropertyModel.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/property/PropertyModel.java @@ -47,7 +47,9 @@ import static org.qi4j.functional.Iterables.empty; import static org.qi4j.functional.Iterables.first; /** - * JAVADOC + * Model for a Property. + * + * <p>Equality is based on the Property accessor object (property type and name), not on the QualifiedName.</p> */ public class PropertyModel implements PropertyDescriptor, PropertyInfo, Binder, Visitable<PropertyModel> http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/ff99cbd0/core/runtime/src/main/java/org/qi4j/runtime/value/ValueInstance.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/main/java/org/qi4j/runtime/value/ValueInstance.java b/core/runtime/src/main/java/org/qi4j/runtime/value/ValueInstance.java index a68d108..ae8a4ab 100644 --- a/core/runtime/src/main/java/org/qi4j/runtime/value/ValueInstance.java +++ b/core/runtime/src/main/java/org/qi4j/runtime/value/ValueInstance.java @@ -47,6 +47,16 @@ public final class ValueInstance super( compositeModel, moduleInstance, mixins, state ); } + /** + * Perform equals with {@code o} argument. + * <p> + * The definition of equals() for the Value is that if both the state and descriptor are equal, + * then the values are equal. + * </p> + * + * @param o The other object to compare. + * @return Returns a {@code boolean} indicator whether this object is equals the other. + */ @Override public boolean equals( Object o ) { @@ -62,6 +72,12 @@ public final class ValueInstance try { ValueInstance that = (ValueInstance) Proxy.getInvocationHandler( o ); + // Descriptor equality + if( !descriptor().equals( that.descriptor() ) ) + { + return false; + } + // State equality return state.equals( that.state ); } catch( ClassCastException e ) @@ -132,10 +148,16 @@ public final class ValueInstance } } + /** + * Calculate hash code. + * + * @return the hashcode of this instance. + */ @Override public int hashCode() { - return state.hashCode(); + int hash = compositeModel.hashCode() * 23; // Descriptor + return hash + state.hashCode() * 5; // State } @Override http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/ff99cbd0/core/runtime/src/test/java/org/qi4j/runtime/association/AssociationEqualityTest.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/test/java/org/qi4j/runtime/association/AssociationEqualityTest.java b/core/runtime/src/test/java/org/qi4j/runtime/association/AssociationEqualityTest.java new file mode 100644 index 0000000..1c3852e --- /dev/null +++ b/core/runtime/src/test/java/org/qi4j/runtime/association/AssociationEqualityTest.java @@ -0,0 +1,388 @@ +/* + * Copyright (c) 2013, Paul Merlin. All Rights Reserved. + * + * Licensed 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.qi4j.runtime.association; + +import org.junit.Test; +import org.qi4j.api.association.Association; +import org.qi4j.api.association.AssociationDescriptor; +import org.qi4j.api.association.ManyAssociation; +import org.qi4j.api.common.Optional; +import org.qi4j.api.unitofwork.UnitOfWork; +import org.qi4j.api.value.ValueBuilder; +import org.qi4j.bootstrap.AssemblyException; +import org.qi4j.bootstrap.ModuleAssembly; +import org.qi4j.test.AbstractQi4jTest; +import org.qi4j.test.EntityTestAssembler; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; + +/** + * Assert that Association and ManyAssociation equals/hashcode methods combine AssociationDescriptor and State. + */ +public class AssociationEqualityTest + extends AbstractQi4jTest +{ + + // + // --------------------------------------:: Types under test ::----------------------------------------------------- + // + @Override + public void assemble( ModuleAssembly module ) + throws AssemblyException + { + new EntityTestAssembler().assemble( module ); + module.entities( AnEntity.class ); + module.values( SomeWithAssociations.class, OtherWithAssociations.class ); + } + + public interface AnEntity + { + } + + public interface SomeWithAssociations + { + + @Optional + Association<AnEntity> anEntity(); + + ManyAssociation<AnEntity> manyEntities(); + } + + public interface OtherWithAssociations + { + + @Optional + Association<AnEntity> anEntity(); + + ManyAssociation<AnEntity> manyEntities(); + } + + // + // ----------------------------:: AssociationDescriptor equality tests ::------------------------------------------- + // + @Test + public void givenValuesOfTheSameTypeAndSameStateWhenTestingAssociationDescriptorEqualityExpectEquals() + { + UnitOfWork uow = module.newUnitOfWork(); + try + { + AnEntity anEntity = uow.newEntity( AnEntity.class ); + + SomeWithAssociations some = buildSomeWithAssociation( anEntity ); + AssociationDescriptor someAssocDesc = qi4j.api().associationDescriptorFor( some.anEntity() ); + AssociationDescriptor someManyAssocDesc = qi4j.api().associationDescriptorFor( some.manyEntities() ); + + SomeWithAssociations some2 = buildSomeWithAssociation( anEntity ); + AssociationDescriptor some2AssocDesc = qi4j.api().associationDescriptorFor( some2.anEntity() ); + AssociationDescriptor some2ManyAssocDesc = qi4j.api().associationDescriptorFor( some2.manyEntities() ); + + assertThat( "AssociationDescriptor equal", + someAssocDesc, + equalTo( some2AssocDesc ) ); + assertThat( "AssociationDescriptor hashcode equal", + someAssocDesc.hashCode(), + equalTo( some2AssocDesc.hashCode() ) ); + assertThat( "ManyAssociationDescriptor equal", + someManyAssocDesc, + equalTo( some2ManyAssocDesc ) ); + assertThat( "ManyAssociationDescriptor hashcode equal", + someManyAssocDesc.hashCode(), + equalTo( some2ManyAssocDesc.hashCode() ) ); + } + finally + { + uow.discard(); + } + } + + @Test + public void givenValuesOfTheSameTypeAndDifferentStateWhenTestingAssociationDescriptorEqualityExpectEquals() + { + UnitOfWork uow = module.newUnitOfWork(); + try + { + SomeWithAssociations some = buildSomeWithAssociation( uow.newEntity( AnEntity.class ) ); + AssociationDescriptor someAssocDesc = qi4j.api().associationDescriptorFor( some.anEntity() ); + AssociationDescriptor someManyAssocDesc = qi4j.api().associationDescriptorFor( some.manyEntities() ); + + SomeWithAssociations some2 = buildSomeWithAssociation( uow.newEntity( AnEntity.class ) ); + AssociationDescriptor some2AssocDesc = qi4j.api().associationDescriptorFor( some2.anEntity() ); + AssociationDescriptor some2ManyAssocDesc = qi4j.api().associationDescriptorFor( some2.manyEntities() ); + + assertThat( "AssociationDescriptor equal", + someAssocDesc, + equalTo( some2AssocDesc ) ); + assertThat( "AssociationDescriptor hashcode equal", + someAssocDesc.hashCode(), + equalTo( some2AssocDesc.hashCode() ) ); + assertThat( "ManyAssociationDescriptor equal", + someManyAssocDesc, + equalTo( some2ManyAssocDesc ) ); + assertThat( "ManyAssociationDescriptor hashcode equal", + someManyAssocDesc.hashCode(), + equalTo( some2ManyAssocDesc.hashCode() ) ); + } + finally + { + uow.discard(); + } + } + + @Test + public void givenValuesOfDifferentTypeAndSameStateWhenTestingAssociationDescriptorEqualityExpectNotEquals() + { + UnitOfWork uow = module.newUnitOfWork(); + try + { + AnEntity anEntity = uow.newEntity( AnEntity.class ); + + SomeWithAssociations some = buildSomeWithAssociation( anEntity ); + AssociationDescriptor someAssocDesc = qi4j.api().associationDescriptorFor( some.anEntity() ); + AssociationDescriptor someManyAssocDesc = qi4j.api().associationDescriptorFor( some.manyEntities() ); + + OtherWithAssociations other = buildOtherWithAssociation( anEntity ); + AssociationDescriptor otherAssocDesc = qi4j.api().associationDescriptorFor( other.anEntity() ); + AssociationDescriptor some2ManyAssocDesc = qi4j.api().associationDescriptorFor( other.manyEntities() ); + + assertThat( "AssociationDescriptor not equal", + someAssocDesc, + not( equalTo( otherAssocDesc ) ) ); + assertThat( "AssociationDescriptor hashcode not equal", + someAssocDesc.hashCode(), + not( equalTo( otherAssocDesc.hashCode() ) ) ); + assertThat( "ManyAssociationDescriptor not equal", + someManyAssocDesc, + not( equalTo( some2ManyAssocDesc ) ) ); + assertThat( "ManyAssociationDescriptor hashcode not equal", + someManyAssocDesc.hashCode(), + not( equalTo( some2ManyAssocDesc.hashCode() ) ) ); + } + finally + { + uow.discard(); + } + } + + // + // --------------------------------:: Association State equality tests ::---------------------------------------------- + // + @Test + public void givenValuesOfSameTypeAndDifferentStateWhenTestingAssociationStateEqualityExpectNotEquals() + { + UnitOfWork uow = module.newUnitOfWork(); + try + { + SomeWithAssociations some = buildSomeWithAssociation( uow.newEntity( AnEntity.class ) ); + SomeWithAssociations some2 = buildSomeWithAssociation( uow.newEntity( AnEntity.class ) ); + + assertThat( "Association State not equal", + some.anEntity().get(), + not( equalTo( some2.anEntity().get() ) ) ); + assertThat( "Association State hashcode not equal", + some.anEntity().get().hashCode(), + not( equalTo( some2.anEntity().get().hashCode() ) ) ); + assertThat( "ManyAssociation State not equal", + some.manyEntities().toList(), + not( equalTo( some2.manyEntities().toList() ) ) ); + assertThat( "ManyAssociation State hashcode not equal", + some.manyEntities().toList().hashCode(), + not( equalTo( some2.manyEntities().toList().hashCode() ) ) ); + } + finally + { + uow.discard(); + } + } + + @Test + public void givenValuesOfDifferentTypesAndSameStateWhenTestingAssociationStateEqualityExpectEquals() + { + UnitOfWork uow = module.newUnitOfWork(); + try + { + AnEntity anEntity = uow.newEntity( AnEntity.class ); + + SomeWithAssociations some = buildSomeWithAssociation( anEntity ); + OtherWithAssociations other = buildOtherWithAssociation( anEntity ); + + assertThat( "Association State equal", + some.anEntity().get(), + equalTo( other.anEntity().get() ) ); + assertThat( "Association State hashcode equal", + some.anEntity().get().hashCode(), + equalTo( other.anEntity().get().hashCode() ) ); + assertThat( "ManyAssociation State equal", + some.manyEntities().toList(), + equalTo( other.manyEntities().toList() ) ); + assertThat( "ManyAssociation State hashcode equal", + some.manyEntities().toList().hashCode(), + equalTo( other.manyEntities().toList().hashCode() ) ); + } + finally + { + uow.discard(); + } + } + + // + // ----------------------------------:: Association equality tests ::----------------------------------------------- + // + @Test + public void givenValuesOfTheSameTypeAndSameStateWhenTestingAssociationEqualityExpectEquals() + { + UnitOfWork uow = module.newUnitOfWork(); + try + { + AnEntity anEntity = uow.newEntity( AnEntity.class ); + + SomeWithAssociations some = buildSomeWithAssociation( anEntity ); + SomeWithAssociations some2 = buildSomeWithAssociation( anEntity ); + + assertThat( "Association equal", + some.anEntity(), + equalTo( some2.anEntity() ) ); + assertThat( "Association hashcode equal", + some.anEntity().hashCode(), + equalTo( some2.anEntity().hashCode() ) ); + assertThat( "ManyAssociation equal", + some.manyEntities(), + equalTo( some2.manyEntities() ) ); + assertThat( "ManyAssociation hashcode equal", + some.manyEntities().hashCode(), + equalTo( some2.manyEntities().hashCode() ) ); + } + finally + { + uow.discard(); + } + } + + @Test + public void givenValuesOfTheSameTypeAndDifferentStateWhenTestingAssociationEqualityExpectNotEquals() + { + UnitOfWork uow = module.newUnitOfWork(); + try + { + SomeWithAssociations some = buildSomeWithAssociation( uow.newEntity( AnEntity.class ) ); + SomeWithAssociations some2 = buildSomeWithAssociation( uow.newEntity( AnEntity.class ) ); + + assertThat( "Association not equal", + some.anEntity(), + not( equalTo( some2.anEntity() ) ) ); + assertThat( "Association hashcode not equal", + some.anEntity().hashCode(), + not( equalTo( some2.anEntity().hashCode() ) ) ); + assertThat( "ManyAssociation not equal", + some.manyEntities(), + not( equalTo( some2.manyEntities() ) ) ); + assertThat( "ManyAssociation hashcode not equal", + some.manyEntities().hashCode(), + not( equalTo( some2.manyEntities().hashCode() ) ) ); + } + finally + { + uow.discard(); + } + } + + @Test + public void givenValuesOfDifferentTypesAndSameStateWhenTestingAssociationEqualityExpectNotEquals() + { + UnitOfWork uow = module.newUnitOfWork(); + try + { + AnEntity anEntity = uow.newEntity( AnEntity.class ); + + SomeWithAssociations some = buildSomeWithAssociation( anEntity ); + OtherWithAssociations other = buildOtherWithAssociation( anEntity ); + + assertThat( "Association not equal", + some.anEntity(), + not( equalTo( other.anEntity() ) ) ); + assertThat( "Association hashcode not equal", + some.anEntity().hashCode(), + not( equalTo( other.anEntity().hashCode() ) ) ); + assertThat( "ManyAssociation not equal", + some.manyEntities(), + not( equalTo( other.manyEntities() ) ) ); + assertThat( "ManyAssociation hashcode not equal", + some.manyEntities().hashCode(), + not( equalTo( other.manyEntities().hashCode() ) ) ); + } + finally + { + uow.discard(); + } + } + + @Test + public void givenValuesOfDifferentTypesAndDifferentStateWhenTestingAssociationEqualityExpectNotEquals() + { + UnitOfWork uow = module.newUnitOfWork(); + try + { + SomeWithAssociations some = buildSomeWithAssociation( uow.newEntity( AnEntity.class ) ); + OtherWithAssociations other = buildOtherWithAssociation( uow.newEntity( AnEntity.class ) ); + + assertThat( "Association not equal", + some.anEntity(), + not( equalTo( other.anEntity() ) ) ); + assertThat( "Association hashcode not equal", + some.anEntity().hashCode(), + not( equalTo( other.anEntity().hashCode() ) ) ); + assertThat( "ManyAssociation not equal", + some.manyEntities(), + not( equalTo( other.manyEntities() ) ) ); + assertThat( "ManyAssociation hashcode not equal", + some.manyEntities().hashCode(), + not( equalTo( other.manyEntities().hashCode() ) ) ); + } + finally + { + uow.discard(); + } + } + + // + // -----------------------------------:: Values factory methods ::-------------------------------------------------- + // + private SomeWithAssociations buildSomeWithAssociation( AnEntity associated ) + { + SomeWithAssociations some; + { + ValueBuilder<SomeWithAssociations> builder = module.newValueBuilder( SomeWithAssociations.class ); + builder.prototype().anEntity().set( associated ); + builder.prototype().manyEntities().add( associated ); + some = builder.newInstance(); + } + return some; + } + + private OtherWithAssociations buildOtherWithAssociation( AnEntity associated ) + { + OtherWithAssociations some; + { + ValueBuilder<OtherWithAssociations> builder = module.newValueBuilder( OtherWithAssociations.class ); + builder.prototype().anEntity().set( associated ); + builder.prototype().manyEntities().add( associated ); + some = builder.newInstance(); + } + return some; + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/ff99cbd0/core/runtime/src/test/java/org/qi4j/runtime/property/PropertyEqualityTest.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/test/java/org/qi4j/runtime/property/PropertyEqualityTest.java b/core/runtime/src/test/java/org/qi4j/runtime/property/PropertyEqualityTest.java new file mode 100644 index 0000000..22de796 --- /dev/null +++ b/core/runtime/src/test/java/org/qi4j/runtime/property/PropertyEqualityTest.java @@ -0,0 +1,430 @@ +/* + * Copyright (c) 2013, Paul Merlin. All Rights Reserved. + * + * Licensed 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.qi4j.runtime.property; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import org.joda.time.DateTime; +import org.joda.time.LocalDate; +import org.joda.time.LocalDateTime; +import org.junit.Test; +import org.qi4j.api.common.Optional; +import org.qi4j.api.property.Property; +import org.qi4j.api.property.PropertyDescriptor; +import org.qi4j.api.structure.Module; +import org.qi4j.api.value.ValueBuilder; +import org.qi4j.api.value.ValueDescriptor; +import org.qi4j.bootstrap.AssemblyException; +import org.qi4j.bootstrap.ModuleAssembly; +import org.qi4j.test.AbstractQi4jTest; + +import static org.hamcrest.CoreMatchers.allOf; +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.not; +import static org.joda.time.DateTimeZone.UTC; +import static org.junit.Assert.assertThat; + +/** + * Assert that Property equals/hashcode methods combine PropertyDescriptor and State. + */ +public class PropertyEqualityTest + extends AbstractQi4jTest +{ + + // + // --------------------------------------:: Types under test ::----------------------------------------------------- + // + @Override + public void assemble( ModuleAssembly module ) + throws AssemblyException + { + module.values( PrimitivesValue.class, Some.class, AnotherSome.class, Other.class ); + } + + public enum AnEnum + { + + BAZAR, CATHEDRAL + } + + public interface PrimitivesValue + { + + Property<Character> characterProperty(); + + Property<String> stringProperty(); + + Property<Boolean> booleanProperty(); + + Property<Integer> integerProperty(); + + Property<Long> longProperty(); + + Property<Float> floatProperty(); + + Property<Double> doubleProperty(); + + Property<Short> shortProperty(); + + Property<Byte> byteProperty(); + + Property<AnEnum> enumProperty(); + } + + public interface Some + extends PrimitivesValue + { + + @Optional + Property<Some> selfProperty(); + + Property<BigInteger> bigIntegerProperty(); + + Property<BigDecimal> bigDecimalProperty(); + + Property<Date> dateProperty(); + + Property<DateTime> dateTimeProperty(); + + Property<LocalDate> localDateProperty(); + + Property<LocalDateTime> localDateTimeProperty(); + } + + public interface AnotherSome + extends Some + { + } + + public interface Other + { + + Property<Character> characterProperty(); + } + + // + // ------------------------------:: PropertyDescriptor equality tests ::-------------------------------------------- + // + @Test + public void givenValuesOfTheSameTypeWhenTestingPropertyDescriptorEqualityExpectEquals() + { + Some some = buildSomeValue( module ); + ValueDescriptor someDescriptor = qi4j.api().valueDescriptorFor( some ); + PropertyDescriptor someCharPropDesc = someDescriptor.state().findPropertyModelByName( "characterProperty" ); + + Some other = buildSomeValue( module ); + ValueDescriptor otherDescriptor = qi4j.api().valueDescriptorFor( other ); + PropertyDescriptor otherCharPropDesc = otherDescriptor.state().findPropertyModelByName( "characterProperty" ); + + assertThat( "PropertyDescriptors equal", + someCharPropDesc, + equalTo( otherCharPropDesc ) ); + assertThat( "PropertyDescriptors hashcode equal", + someCharPropDesc.hashCode(), + equalTo( otherCharPropDesc.hashCode() ) ); + } + + @Test + public void givenValuesOfCommonTypesWhenTestingPropertyDescriptorEqualityExpectEquals() + { + Some some = buildSomeValue( module ); + ValueDescriptor someDescriptor = qi4j.api().valueDescriptorFor( some ); + PropertyDescriptor someCharPropDesc = someDescriptor.state().findPropertyModelByName( "characterProperty" ); + + PrimitivesValue primitive = buildPrimitivesValue( module ); + ValueDescriptor primitiveDescriptor = qi4j.api().valueDescriptorFor( primitive ); + PropertyDescriptor primitiveCharPropDesc = primitiveDescriptor.state().findPropertyModelByName( "characterProperty" ); + + assertThat( "PropertyDescriptors equal", + someCharPropDesc, + equalTo( primitiveCharPropDesc ) ); + assertThat( "PropertyDescriptors hashcode equal", + someCharPropDesc.hashCode(), + equalTo( primitiveCharPropDesc.hashCode() ) ); + } + + @Test + public void givenValuesOfDifferentTypesWhenTestingPropertyDescriptorEqualityExpectNotEquals() + { + Some some = buildSomeValue( module ); + ValueDescriptor someDescriptor = qi4j.api().valueDescriptorFor( some ); + PropertyDescriptor someCharPropDesc = someDescriptor.state().findPropertyModelByName( "characterProperty" ); + + Other other = buildOtherValue( module ); + ValueDescriptor otherDescriptor = qi4j.api().valueDescriptorFor( other ); + PropertyDescriptor otherCharPropDesc = otherDescriptor.state().findPropertyModelByName( "characterProperty" ); + + assertThat( "PropertyDescriptors not equal", + someCharPropDesc, + not( equalTo( otherCharPropDesc ) ) ); + assertThat( "PropertyDescriptors hashcode not equal", + someCharPropDesc.hashCode(), + not( equalTo( otherCharPropDesc.hashCode() ) ) ); + } + + // + // --------------------------------:: Property State equality tests ::---------------------------------------------- + // + @Test + public void givenValuesOfDifferentTypesAndSameStateWhenTestingPropertyStateEqualityExpectEquals() + { + PrimitivesValue primitives = buildPrimitivesValue( module ); + Some some = buildSomeValue( module ); + Some some2 = buildSomeValue( module ); + Other other = buildOtherValue( module ); + assertThat( "Property state equal", + 'q', + allOf( equalTo( primitives.characterProperty().get() ), + equalTo( some.characterProperty().get() ), + equalTo( some2.characterProperty().get() ), + equalTo( other.characterProperty().get() ) ) ); + assertThat( "Property state hashcode equal", + new Character( 'q' ).hashCode(), + allOf( equalTo( primitives.characterProperty().get().hashCode() ), + equalTo( some.characterProperty().get().hashCode() ), + equalTo( some2.characterProperty().get().hashCode() ), + equalTo( other.characterProperty().get().hashCode() ) ) ); + } + + // + // -----------------------------------:: Property equality tests ::------------------------------------------------- + // + @Test + public void givenValuesOfTheSameTypeAndSameStateWhenTestingPropertyEqualityExpectEquals() + { + Some some = buildSomeValue( module ); + Some some2 = buildSomeValue( module ); + assertThat( "Property equals", + some.characterProperty(), + equalTo( some2.characterProperty() ) ); + assertThat( "Property hashcode equals", + some.characterProperty().hashCode(), + equalTo( some2.characterProperty().hashCode() ) ); + } + + @Test + public void givenValuesOfTheSameTypeWithDifferentStateWhenTestingPropertyEqualityExpectNotEquals() + { + Some some = buildSomeValue( module ); + Some some2 = buildSomeValueWithDifferentState( module ); + assertThat( "Property not equals", + some.characterProperty(), + not( equalTo( some2.characterProperty() ) ) ); + assertThat( "Property hashcode not equals", + some.characterProperty().hashCode(), + not( equalTo( some2.characterProperty().hashCode() ) ) ); + } + + @Test + public void givenValuesOfCommonTypesAndSameStateWhenTestingPropertyEqualityExpectEquals() + { + Some some = buildSomeValue( module ); + PrimitivesValue primitive = buildPrimitivesValue( module ); + assertThat( "Property equal", + some.characterProperty(), + equalTo( primitive.characterProperty() ) ); + } + + @Test + public void givenValuesOfCommonTypesWithDifferentStateWhenTestingPropertyEqualityExpectNotEquals() + { + Some some = buildSomeValue( module ); + PrimitivesValue primitive = buildPrimitivesValueWithDifferentState( module ); + assertThat( "Property not equal", + some.characterProperty(), + not( equalTo( primitive.characterProperty() ) ) ); + } + + @Test + public void givenValuesOfDifferentTypesAndSameStateWhenTestingPropertyEqualityExpectNotEquals() + { + Some some = buildSomeValue( module ); + Other other = buildOtherValue( module ); + assertThat( "Property not equal", + some.characterProperty(), + not( equalTo( other.characterProperty() ) ) ); + } + + @Test + public void givenValuesOfDifferentTypesWithDifferentStateWhenTestingPropertyEqualityExpectNotEquals() + { + Some some = buildSomeValue( module ); + Other other = buildOtherValue( module ); + assertThat( "Property not equal", + some.characterProperty(), + not( equalTo( other.characterProperty() ) ) ); + } + + // + // -----------------------------------:: Values factory methods ::-------------------------------------------------- + // + public static PrimitivesValue buildPrimitivesValue( Module module ) + { + PrimitivesValue primitive; + { + ValueBuilder<PrimitivesValue> builder = module.newValueBuilder( PrimitivesValue.class ); + builder.prototype().characterProperty().set( 'q' ); + builder.prototype().stringProperty().set( "foo" ); + builder.prototype().booleanProperty().set( true ); + builder.prototype().integerProperty().set( 42 ); + builder.prototype().longProperty().set( 42L ); + builder.prototype().floatProperty().set( 42.23F ); + builder.prototype().doubleProperty().set( 42.23D ); + builder.prototype().shortProperty().set( (short) 42 ); + builder.prototype().byteProperty().set( (byte) 42 ); + builder.prototype().enumProperty().set( AnEnum.BAZAR ); + primitive = builder.newInstance(); + } + return primitive; + } + + public static PrimitivesValue buildPrimitivesValueWithDifferentState( Module module ) + { + PrimitivesValue primitive; + { + ValueBuilder<PrimitivesValue> builder = module.newValueBuilder( PrimitivesValue.class ); + builder.prototype().characterProperty().set( 'i' ); + builder.prototype().stringProperty().set( "bar" ); + builder.prototype().booleanProperty().set( false ); + builder.prototype().integerProperty().set( 23 ); + builder.prototype().longProperty().set( 23L ); + builder.prototype().floatProperty().set( 23.42F ); + builder.prototype().doubleProperty().set( 23.42D ); + builder.prototype().shortProperty().set( (short) 23 ); + builder.prototype().byteProperty().set( (byte) 23 ); + builder.prototype().enumProperty().set( AnEnum.CATHEDRAL ); + primitive = builder.newInstance(); + } + return primitive; + } + + public static Some buildSomeValue( Module module ) + { + Some some; + { + ValueBuilder<Some> builder = module.newValueBuilder( Some.class ); + builder.prototype().characterProperty().set( 'q' ); + builder.prototype().stringProperty().set( "foo" ); + builder.prototype().booleanProperty().set( true ); + builder.prototype().integerProperty().set( 42 ); + builder.prototype().longProperty().set( 42L ); + builder.prototype().floatProperty().set( 42.23F ); + builder.prototype().doubleProperty().set( 42.23D ); + builder.prototype().shortProperty().set( (short) 42 ); + builder.prototype().byteProperty().set( (byte) 42 ); + builder.prototype().enumProperty().set( AnEnum.BAZAR ); + builder.prototype().bigIntegerProperty().set( new BigInteger( "42" ) ); + builder.prototype().bigDecimalProperty().set( new BigDecimal( "42.23" ) ); + builder.prototype().dateProperty().set( new DateTime( "2020-03-04T13:24:35", UTC ).toDate() ); + builder.prototype().dateTimeProperty().set( new DateTime( "2020-03-04T13:24:35", UTC ) ); + builder.prototype().localDateProperty().set( new LocalDate( "2020-03-04" ) ); + builder.prototype().localDateTimeProperty().set( new LocalDateTime( "2020-03-04T13:23:00", UTC ) ); + some = builder.newInstance(); + } + return some; + } + + public static Some buildSomeValueWithDifferentState( Module module ) + { + Some some; + { + ValueBuilder<Some> builder = module.newValueBuilder( Some.class ); + builder.prototype().characterProperty().set( 'i' ); + builder.prototype().stringProperty().set( "bar" ); + builder.prototype().booleanProperty().set( false ); + builder.prototype().integerProperty().set( 23 ); + builder.prototype().longProperty().set( 23L ); + builder.prototype().floatProperty().set( 23.42F ); + builder.prototype().doubleProperty().set( 23.42D ); + builder.prototype().shortProperty().set( (short) 23 ); + builder.prototype().byteProperty().set( (byte) 23 ); + builder.prototype().enumProperty().set( AnEnum.CATHEDRAL ); + builder.prototype().bigIntegerProperty().set( new BigInteger( "23" ) ); + builder.prototype().bigDecimalProperty().set( new BigDecimal( "23.42" ) ); + builder.prototype().dateProperty().set( new DateTime( "2030-02-08T09:09:09", UTC ).toDate() ); + builder.prototype().dateTimeProperty().set( new DateTime( "2030-02-08T09:09:09", UTC ) ); + builder.prototype().localDateProperty().set( new LocalDate( "2030-02-08" ) ); + builder.prototype().localDateTimeProperty().set( new LocalDateTime( "2030-02-08T09:09:09", UTC ) ); + some = builder.newInstance(); + } + return some; + } + + public static AnotherSome buildAnotherSomeValue( Module module ) + { + AnotherSome anotherSome; + { + ValueBuilder<AnotherSome> builder = module.newValueBuilder( AnotherSome.class ); + builder.prototype().characterProperty().set( 'q' ); + builder.prototype().stringProperty().set( "foo" ); + builder.prototype().booleanProperty().set( true ); + builder.prototype().integerProperty().set( 42 ); + builder.prototype().longProperty().set( 42L ); + builder.prototype().floatProperty().set( 42.23F ); + builder.prototype().doubleProperty().set( 42.23D ); + builder.prototype().shortProperty().set( (short) 42 ); + builder.prototype().byteProperty().set( (byte) 42 ); + builder.prototype().enumProperty().set( AnEnum.BAZAR ); + builder.prototype().bigIntegerProperty().set( new BigInteger( "42" ) ); + builder.prototype().bigDecimalProperty().set( new BigDecimal( "42.23" ) ); + builder.prototype().dateProperty().set( new DateTime( "2020-03-04T13:24:35", UTC ).toDate() ); + builder.prototype().dateTimeProperty().set( new DateTime( "2020-03-04T13:24:35", UTC ) ); + builder.prototype().localDateProperty().set( new LocalDate( "2020-03-04" ) ); + builder.prototype().localDateTimeProperty().set( new LocalDateTime( "2020-03-04T13:23:00", UTC ) ); + anotherSome = builder.newInstance(); + } + return anotherSome; + } + + public static AnotherSome buildAnotherSomeValueWithDifferentState( Module module ) + { + AnotherSome anotherSome; + { + ValueBuilder<AnotherSome> builder = module.newValueBuilder( AnotherSome.class ); + builder.prototype().characterProperty().set( 'i' ); + builder.prototype().stringProperty().set( "bar" ); + builder.prototype().booleanProperty().set( false ); + builder.prototype().integerProperty().set( 23 ); + builder.prototype().longProperty().set( 23L ); + builder.prototype().floatProperty().set( 23.42F ); + builder.prototype().doubleProperty().set( 23.42D ); + builder.prototype().shortProperty().set( (short) 23 ); + builder.prototype().byteProperty().set( (byte) 23 ); + builder.prototype().enumProperty().set( AnEnum.CATHEDRAL ); + builder.prototype().bigIntegerProperty().set( new BigInteger( "23" ) ); + builder.prototype().bigDecimalProperty().set( new BigDecimal( "23.42" ) ); + builder.prototype().dateProperty().set( new DateTime( "2030-02-08T09:09:09", UTC ).toDate() ); + builder.prototype().dateTimeProperty().set( new DateTime( "2030-02-08T09:09:09", UTC ) ); + builder.prototype().localDateProperty().set( new LocalDate( "2030-02-08" ) ); + builder.prototype().localDateTimeProperty().set( new LocalDateTime( "2030-02-08T09:09:09", UTC ) ); + anotherSome = builder.newInstance(); + } + return anotherSome; + } + + public static Other buildOtherValue( Module module ) + { + Other other; + { + ValueBuilder<Other> builder = module.newValueBuilder( Other.class ); + builder.prototype().characterProperty().set( 'q' ); + other = builder.newInstance(); + } + return other; + } +} http://git-wip-us.apache.org/repos/asf/zest-qi4j/blob/ff99cbd0/core/runtime/src/test/java/org/qi4j/runtime/value/ValueEqualityTest.java ---------------------------------------------------------------------- diff --git a/core/runtime/src/test/java/org/qi4j/runtime/value/ValueEqualityTest.java b/core/runtime/src/test/java/org/qi4j/runtime/value/ValueEqualityTest.java new file mode 100644 index 0000000..e0df77a --- /dev/null +++ b/core/runtime/src/test/java/org/qi4j/runtime/value/ValueEqualityTest.java @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2013, Paul Merlin. All Rights Reserved. + * + * Licensed 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.qi4j.runtime.value; + +import org.junit.Test; +import org.qi4j.api.association.AssociationStateHolder; +import org.qi4j.api.value.ValueComposite; +import org.qi4j.api.value.ValueDescriptor; +import org.qi4j.bootstrap.AssemblyException; +import org.qi4j.bootstrap.ModuleAssembly; +import org.qi4j.runtime.property.PropertyEqualityTest.AnotherSome; +import org.qi4j.runtime.property.PropertyEqualityTest.Other; +import org.qi4j.runtime.property.PropertyEqualityTest.PrimitivesValue; +import org.qi4j.runtime.property.PropertyEqualityTest.Some; +import org.qi4j.test.AbstractQi4jTest; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.not; +import static org.junit.Assert.assertThat; +import static org.qi4j.runtime.property.PropertyEqualityTest.buildAnotherSomeValue; +import static org.qi4j.runtime.property.PropertyEqualityTest.buildAnotherSomeValueWithDifferentState; +import static org.qi4j.runtime.property.PropertyEqualityTest.buildOtherValue; +import static org.qi4j.runtime.property.PropertyEqualityTest.buildPrimitivesValue; +import static org.qi4j.runtime.property.PropertyEqualityTest.buildSomeValue; +import static org.qi4j.runtime.property.PropertyEqualityTest.buildSomeValueWithDifferentState; + +/** + * Assert that Value equals/hashcode methods combine ValueDescriptor and ValueState. + */ +public class ValueEqualityTest + extends AbstractQi4jTest +{ + + // + // --------------------------------------:: Types under test ::----------------------------------------------------- + // + @Override + public void assemble( ModuleAssembly module ) + throws AssemblyException + { + module.values( PrimitivesValue.class, Some.class, AnotherSome.class, Other.class ); + } + + // + // -------------------------------:: ValueDescriptor equality tests ::---------------------------------------------- + // + @Test + public void givenValuesOfTheSameTypeWhenTestingValueDescriptorEqualityExpectEquals() + { + Some some = buildSomeValue( module ); + ValueDescriptor someDescriptor = qi4j.api().valueDescriptorFor( some ); + + Some other = buildSomeValue( module ); + ValueDescriptor otherDescriptor = qi4j.api().valueDescriptorFor( other ); + + assertThat( "ValueDescriptors equal", + someDescriptor, + equalTo( otherDescriptor ) ); + assertThat( "ValueDescriptors hashcode equal", + someDescriptor.hashCode(), + equalTo( otherDescriptor.hashCode() ) ); + } + + @Test + public void givenValuesOfCommonTypesWhenTestingValueDescriptorEqualityExpectNotEquals() + { + Some some = buildSomeValue( module ); + ValueDescriptor someDescriptor = qi4j.api().valueDescriptorFor( some ); + + PrimitivesValue primitive = buildPrimitivesValue( module ); + ValueDescriptor primitiveDescriptor = qi4j.api().valueDescriptorFor( primitive ); + + assertThat( "ValueDescriptors not equal", + someDescriptor, + not( equalTo( primitiveDescriptor ) ) ); + assertThat( "ValueDescriptors hashcode not equal", + someDescriptor.hashCode(), + not( equalTo( primitiveDescriptor.hashCode() ) ) ); + } + + @Test + public void givenValuesOfDifferentTypesWhenTestingValueDescriptorEqualityExpectNotEquals() + { + Some some = buildSomeValue( module ); + ValueDescriptor someDescriptor = qi4j.api().valueDescriptorFor( some ); + + Other other = buildOtherValue( module ); + ValueDescriptor otherDescriptor = qi4j.api().valueDescriptorFor( other ); + + assertThat( "ValueDescriptors not equal", + someDescriptor, + not( equalTo( otherDescriptor ) ) ); + assertThat( "ValueDescriptors hashcode not equal", + someDescriptor.hashCode(), + not( equalTo( otherDescriptor.hashCode() ) ) ); + } + + // + // ---------------------------------:: Value State equality tests ::------------------------------------------------ + // + @Test + public void givenValuesOfSameTypesAndSameStateWhenTestingValueStateEqualityExpectEquals() + { + Some some = buildSomeValue( module ); + AssociationStateHolder someState = qi4j.spi().stateOf( (ValueComposite) some ); + + Some some2 = buildSomeValue( module ); + AssociationStateHolder some2State = qi4j.spi().stateOf( (ValueComposite) some2 ); + + assertThat( "ValueStates equal", + someState, + equalTo( some2State ) ); + assertThat( "ValueStates hashcode equal", + someState.hashCode(), + equalTo( some2State.hashCode() ) ); + } + + @Test + public void givenValuesOfSameTypesAndDifferentStateWhenTestingValueStateEqualityExpectNotEquals() + { + Some some = buildSomeValue( module ); + AssociationStateHolder someState = qi4j.spi().stateOf( (ValueComposite) some ); + + Some some2 = buildSomeValueWithDifferentState( module ); + AssociationStateHolder some2State = qi4j.spi().stateOf( (ValueComposite) some2 ); + + assertThat( "ValueStates not equal", + someState, + not( equalTo( some2State ) ) ); + assertThat( "ValueStates hashcode not equal", + someState.hashCode(), + not( equalTo( some2State.hashCode() ) ) ); + } + + @Test + public void givenValuesOfDifferentTypesAndSameStateWhenTestingValueStateEqualityExpectEquals() + { + Some some = buildSomeValue( module ); + AssociationStateHolder someState = qi4j.spi().stateOf( (ValueComposite) some ); + + AnotherSome anotherSome = buildAnotherSomeValue( module ); + AssociationStateHolder anotherSomeState = qi4j.spi().stateOf( (ValueComposite) anotherSome ); + + assertThat( "ValueStates equal", + someState, + equalTo( anotherSomeState ) ); + assertThat( "ValueStates hashcode equal", + someState.hashCode(), + equalTo( anotherSomeState.hashCode() ) ); + } + + @Test + public void givenValuesOfDifferentTypesAndDifferentStateWhenTestingValueStateEqualityExpectNotEquals() + { + Some some = buildSomeValue( module ); + AssociationStateHolder someState = qi4j.spi().stateOf( (ValueComposite) some ); + + AnotherSome anotherSome = buildAnotherSomeValueWithDifferentState( module ); + AssociationStateHolder anotherSomeState = qi4j.spi().stateOf( (ValueComposite) anotherSome ); + + assertThat( "ValueStates not equal", + someState, + not( equalTo( anotherSomeState ) ) ); + assertThat( "ValueStates hashcode not equal", + someState.hashCode(), + not( equalTo( anotherSomeState.hashCode() ) ) ); + } + + // + // ------------------------------------:: Value equality tests ::--------------------------------------------------- + // + @Test + public void givenValuesOfSameTypesAndSameStateWhenTestingValueEqualityExpectEquals() + { + Some some = buildSomeValue( module ); + Some some2 = buildSomeValue( module ); + assertThat( "Values equal", + some, + equalTo( some2 ) ); + assertThat( "Values hashcode equal", + some.hashCode(), + equalTo( some2.hashCode() ) ); + } + + @Test + public void givenValuesOfTheSameTypeWithDifferentStateWhenTestingValueEqualityExpectNotEquals() + { + Some some = buildSomeValue( module ); + Some some2 = buildSomeValueWithDifferentState( module ); + assertThat( "Values not equals", + some, + not( equalTo( some2 ) ) ); + assertThat( "Values hashcode not equals", + some.hashCode(), + not( equalTo( some2.hashCode() ) ) ); + } + + @Test + public void givenValuesOfDifferentTypesAndSameStateWhenTestingValueEqualityExpectNotEquals() + { + Some some = buildSomeValue( module ); + Some anotherSome = buildAnotherSomeValue( module ); + + assertThat( "Values not equal", + some, + not( equalTo( anotherSome ) ) ); + assertThat( "Values hashcode not equal", + some.hashCode(), + not( equalTo( anotherSome.hashCode() ) ) ); + } + + @Test + public void givenValuesOfDifferentTypesAndDifferentStateWhenTestingValueEqualityExpectNotEquals() + { + Some some = buildSomeValue( module ); + Some anotherSome = buildAnotherSomeValueWithDifferentState( module ); + assertThat( "Values not equal", + some, + not( equalTo( anotherSome ) ) ); + assertThat( "Values hashcode not equal", + some.hashCode(), + not( equalTo( anotherSome.hashCode() ) ) ); + } +}
