/* * This file is part of LibrePlan * * Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e * Desenvolvemento Tecnolóxico de Galicia * Copyright (C) 2010-2011 Igalia, S.L. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.libreplan.business.util.deepcopy; import static java.util.Arrays.asList; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.everyItem; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.nullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.math.BigDecimal; import java.util.ArrayList; import java.util.Arrays; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.Collections; import org.hamcrest.BaseMatcher; import org.hamcrest.Description; import org.hamcrest.Matcher; import org.joda.time.DateTime; import org.joda.time.LocalDate; import org.junit.Test; import org.libreplan.business.util.deepcopy.EntityExamples.EntityA; import org.libreplan.business.util.deepcopy.EntityExamples.EntityWithoutNoArgsConstructor; import org.libreplan.business.util.deepcopy.EntityExamples.Parent; import org.libreplan.business.util.deepcopy.EntityExamples.SubClassExample; import org.libreplan.business.util.deepcopy.EntityExamples.TestEnum; /** * @author Óscar González Fernández <ogonzalez@igalia.com> */ public class DeepCopyTest { @Test public void theCopyOfANullObjectIsANullObject() { assertThat(new DeepCopy().copy(null), equalTo(null)); } @Test(expected = IllegalArgumentException.class) public void theEntityToCopyMustHaveNotEmptyConstructor() { EntityWithoutNoArgsConstructor entity = new EntityWithoutNoArgsConstructor("bla"); new DeepCopy().copy(entity); } @Test public void stringPropertiesAreShared() { EntityA entityA = new EntityA(); entityA.setStringProperty("foo"); EntityA copy = new DeepCopy().copy(entityA); assertSame(entityA.getStringProperty(), copy.getStringProperty()); } @Test public void intPropertiesAreShared() { EntityA entityA = new EntityA(); EntityA copy = new DeepCopy().copy(entityA); assertEquals(entityA.getIntProperty(), copy.getIntProperty()); } @Test public void nullPropertiesKeepBeingNull() { EntityA entityA = new EntityA(); EntityA copy = new DeepCopy().copy(entityA); assertThat(copy.getNullProperty(), nullValue()); } @Test public void enumPropertiesAreShared() { EntityA entityA = new EntityA(); entityA.setEnumProperty(TestEnum.A); EntityA copy = new DeepCopy().copy(entityA); assertSame(TestEnum.A, copy.getEnumProperty()); } @Test public void itKnowsSomeTypesAreImmutable() { Iterable<Class<?>> immutableTypes = Arrays.asList( String.class, BigDecimal.class, Double.class, Float.class, Integer.class, Short.class, Byte.class, Character.class, LocalDate.class, Boolean.class, DateTime.class, double.class, float.class, int.class, short.class, byte.class, char.class); assertThat(immutableTypes, everyItem(immutable())); } private Matcher<Class<?>> immutable() { return new BaseMatcher<Class<?>>() { @Override public boolean matches(Object value) { return DeepCopy.isImmutableType((Class<?>) value); } @Override public void describeTo(Description description) { description.appendText("is immutable"); } }; } @Test public void itCopiesInheritedPropertiesToo() { SubClassExample subClassExample = new SubClassExample(); subClassExample.setSuperClassStringProperty("foo"); SubClassExample copy = new DeepCopy().copy(subClassExample); assertThat(copy.getSuperClassStringProperty(), equalTo("foo")); } @Test public void datesAreCopied() { EntityA entityA = new EntityA(); Date originalDateValue = new Date(); entityA.setDate(originalDateValue); EntityA copy = new DeepCopy().copy(entityA); assertThat(copy.getDate(), equalTo(originalDateValue)); assertNotSame(originalDateValue, copy.getDate()); } @Test public void setsAreCopied() { EntityA entityA = new EntityA(); HashSet<Object> originalSet = new HashSet<>(asList("test", 2, 3, new Date())); entityA.setSetProperty(originalSet); EntityA copy = new DeepCopy().copy(entityA); assertEquals(originalSet, copy.getSetProperty()); assertNotSame(originalSet, copy.getSetProperty()); } @Test public void theSetImplementationClassIsPreservedIfPossible() { EntityA entityA = new EntityA(); Set<Object> originalSet = new LinkedHashSet<>(asList("test", 2, 3, new Date())); entityA.setSetProperty(originalSet); EntityA copy = new DeepCopy().copy(entityA); assertThat(copy.getSetProperty(), instanceOf(LinkedHashSet.class)); } @Test public void setsInsideSetsAreRecursivelyCopiedWithoutProblem() { EntityA entityA = new EntityA(); HashSet<Object> innerSet = new HashSet<>(asList("bla", 3)); HashSet<Object> originalSet = new HashSet<>(asList("test", 2, 3, new Date(), innerSet)); entityA.setSetProperty(originalSet); EntityA copy = new DeepCopy().copy(entityA); assertEquals(originalSet, copy.getSetProperty()); assertNotSame(originalSet, copy.getSetProperty()); } @Test public void mapsAreCopied() { EntityA entityA = new EntityA(); HashMap<Object, Object> originalMap = new HashMap<>(); originalMap.put("aa", "blabla"); entityA.setMapProperty(originalMap); EntityA copy = new DeepCopy().copy(entityA); assertEquals(originalMap, copy.getMapProperty()); assertNotSame(originalMap, copy.getMapProperty()); } @Test public void mapImplementationIsPreservedIfPossible() { EntityA entityA = new EntityA(); LinkedHashMap<Object, Object> mapProperty = new LinkedHashMap<>(); mapProperty.put("ab", "abc"); entityA.setMapProperty(mapProperty); EntityA copy = new DeepCopy().copy(entityA); assertThat(copy.getMapProperty(), instanceOf(LinkedHashMap.class)); } @Test public void listsAreCopied() { EntityA entityA = new EntityA(); ArrayList<Object> originalList = new ArrayList<>(); originalList.add(2); originalList.add(10); originalList.add("abla"); entityA.setListProperty(originalList); EntityA copy = new DeepCopy().copy(entityA); assertEquals(originalList, copy.getListProperty()); assertNotSame(originalList, copy.getListProperty()); } @Test public void listImplementationIsPreservedIfPossible() { EntityA entityA = new EntityA(); LinkedList<Object> originalList = new LinkedList<>(); originalList.add(2); entityA.setListProperty(originalList); EntityA copy = new DeepCopy().copy(entityA); assertThat(copy.getListProperty(), instanceOf(LinkedList.class)); } @Test public void ignoredFieldsAreNotCopied() { EntityA entityA = new EntityA(); entityA.setIgnoredProperty("blabla"); EntityA copy = new DeepCopy().copy(entityA); assertThat(copy.getIgnoredProperty(), nullValue()); } @Test public void sharedFieldsAreCopiedWithTheSameReference() { EntityA entityA = new EntityA(); Date originalDate = new Date(); entityA.setSharedProperty(originalDate); EntityA copy = new DeepCopy().copy(entityA); assertSame(originalDate, copy.getSharedProperty()); } @Test public void sharedCollectionsAreCopiedWithTheSameReference() { EntityA entityA = new EntityA(); List<String> originalList = Collections.singletonList("bla"); entityA.setSharedListProperty(originalList); EntityA copy = new DeepCopy().copy(entityA); assertSame(originalList, copy.getSharedListProperty()); } @Test public void sharedCollectionElementsKeptTheReferences() { EntityA entityA = new EntityA(); HashSet<Object> originalSet = new HashSet<>(); originalSet.add(new Date()); entityA.setSharedElementsProperty(originalSet); EntityA copy = new DeepCopy().copy(entityA); assertNotSame(originalSet, copy.getSharedElementsProperty()); assertSame(originalSet.iterator().next(), copy.getSharedElementsProperty().iterator().next()); } @Test public void sharedKeyElementsKeepTheSameReferencesForTheKeys() { EntityA entityA = new EntityA(); Map<Object, Object> originalMap = new HashMap<>(); EntityA originalValue = new EntityA(); Date originalKey = new Date(); originalMap.put(originalKey, originalValue); entityA.setSharedKeysMapProperty(originalMap); EntityA copy = new DeepCopy().copy(entityA); Map<Object, Object> sharedKeysMapProperty = copy.getSharedKeysMapProperty(); assertSame(originalKey, sharedKeysMapProperty.keySet().iterator().next()); assertNotSame(originalValue, sharedKeysMapProperty.values().iterator().next()); } @Test public void sharedValueElementsKeepTheSameReferencesForTheValues() { EntityA entityA = new EntityA(); Map<Object, Object> originalMap = new HashMap<>(); EntityA originalValue = new EntityA(); Date originalKey = new Date(); originalMap.put(originalKey, originalValue); entityA.setSharedValuesMapProperty(originalMap); EntityA copy = new DeepCopy().copy(entityA); Map<Object, Object> copiedMap = copy.getSharedValuesMapProperty(); assertNotSame(originalKey, copiedMap.keySet().iterator().next()); assertSame(originalValue, copiedMap.values().iterator().next()); } @Test public void aSharedCollectionElementsMapKeepTheSameReferencesForTheKeysAndTheValues() { EntityA entityA = new EntityA(); Map<Object, Object> originalMap = new HashMap<>(); EntityA originalValue = new EntityA(); Date originalKey = new Date(); originalMap.put(originalKey, originalValue); entityA.setSharedCollectionElementsMapProperty(originalMap); EntityA copy = new DeepCopy().copy(entityA); Map<Object, Object> copiedMap = copy.getSharedCollectionElementsMapProperty(); assertSame(originalKey, copiedMap.keySet().iterator().next()); assertSame(originalValue, copiedMap.values().iterator().next()); } @Test public void ifNotInnmutableNorCustomCopyRecursivelyCopiesIt() { Parent parent = new Parent(); EntityA entityAProperty = new EntityA(); Date originalDate = new Date(); entityAProperty.setDate(originalDate); parent.setEntityAProperty(entityAProperty); Parent copy = new DeepCopy().copy(parent); assertNotSame(parent, copy); assertThat(copy.getEntityAProperty().getDate(), equalTo(originalDate)); assertNotSame(copy.getEntityAProperty().getDate(), originalDate); } @Test public void alreadyCopiedInstancesAreReused() { Parent parent = new Parent(); EntityA entityA = new EntityA(); parent.setEntityAProperty(entityA); entityA.setParentProperty(parent); Parent copy = new DeepCopy().copy(parent); assertSame(copy, copy.getEntityAProperty().getParentProperty()); } @Test public void alreadyCopiedSetsAreReused() { Parent parent = new Parent(); EntityA entityA = new EntityA(); parent.setEntityAProperty(entityA); entityA.setParentProperty(parent); HashSet<Object> originalSet = new HashSet<>(); parent.setSetProperty(originalSet); entityA.setSetProperty(originalSet); Parent copy = new DeepCopy().copy(parent); assertSame(copy.getSetProperty(), copy.getEntityAProperty().getSetProperty()); } @Test public void canSpecifyReplacements() { DeepCopy deepCopy = new DeepCopy(); Parent parent = new Parent(); EntityA entityA = new EntityA(); parent.setEntityAProperty(entityA); EntityA anotherEntity = new EntityA(); deepCopy.replace(entityA, anotherEntity); Parent copy = deepCopy.copy(parent); assertSame(copy.getEntityAProperty(), anotherEntity); } @Test public void afterCopyHooksCanBeDefined() { DeepCopy deepCopy = new DeepCopy(); EntityA entityA = new EntityA(); EntityA copy = deepCopy.copy(entityA); assertTrue(copy.isFirstHookCalled()); assertTrue(copy.isSecondHookCalled()); } @Test public void superclassAfterHooksAreCalled() { DeepCopy deepCopy = new DeepCopy(); SubClassExample subclass = deepCopy.copy(new SubClassExample()); assertTrue(subclass.isAfterCopyHookCalled()); } @Test public void equalObjectsButDifferentAreNotReused() { EntityA entityA = new EntityA(); DeepCopy deepCopy = new DeepCopy(); entityA.setSet1(new HashSet<>()); entityA.setSet2(new HashSet<>()); EntityA copied = deepCopy.copy(entityA); assertNotSame(copied.getSet1(), copied.getSet2()); } }