You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@polygene.apache.org by ni...@apache.org on 2015/04/17 18:05:52 UTC
[32/50] [abbrv] zest-qi4j git commit: Core Runtime: Fix Property and
Value equality
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 <pa...@nosphere.org>
Authored: Tue Feb 19 15:26:17 2013 +0100
Committer: Paul Merlin <pa...@nosphere.org>
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() ) ) );
+ }
+}