/******************************************************************************* * Copyright (c) 2015, 2016 Google, Inc and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Stefan Xenos (Google) - Initial implementation *******************************************************************************/ package org.eclipse.jdt.core.tests.nd; import org.eclipse.jdt.core.tests.nd.util.BaseTestCase; import org.eclipse.jdt.internal.core.nd.Nd; import org.eclipse.jdt.internal.core.nd.NdNode; import org.eclipse.jdt.internal.core.nd.NdNodeTypeRegistry; import org.eclipse.jdt.internal.core.nd.field.FieldManyToOne; import org.eclipse.jdt.internal.core.nd.field.FieldOneToMany; import org.eclipse.jdt.internal.core.nd.field.StructDef; import junit.framework.Test; public class InheritenceTests extends BaseTestCase { /** * Every other object in this test has a pointer to the deletion detector, so we can detect * which objects have been deleted by looking for the object in the backpointer list. */ public static class AllObjects extends NdNode { public static final FieldOneToMany<BaseClass> BASE_CLASS_INSTANCES; public static final FieldOneToMany<Reference> REFERENCE_INSTANCES; @SuppressWarnings("hiding") public static final StructDef<AllObjects> type; static { type = StructDef.create(AllObjects.class, NdNode.type); BASE_CLASS_INSTANCES = FieldOneToMany.create(type, BaseClass.DELETION_DETECTOR, 0); REFERENCE_INSTANCES = FieldOneToMany.create(type, Reference.DELETION_DETECTOR, 0); type.done(); } public AllObjects(Nd nd, long record) { super(nd, record); } public AllObjects(Nd nd) { super(nd); } boolean contains(BaseClass toTest) { return BASE_CLASS_INSTANCES.asList(getNd(), this.address).contains(toTest); } boolean contains(Reference toTest) { return REFERENCE_INSTANCES.asList(getNd(), this.address).contains(toTest); } } public static class BaseClass extends NdNode { public static final FieldOneToMany<Reference> INCOMING_REFERENCES; public static final FieldOneToMany<Reference> OWNED_REFERENCES; public static final FieldManyToOne<AllObjects> DELETION_DETECTOR; @SuppressWarnings("hiding") public static final StructDef<BaseClass> type; static { type = StructDef.create(BaseClass.class, NdNode.type); INCOMING_REFERENCES = FieldOneToMany.create(type, Reference.BASE_CLASS_REFERENCE, 0); OWNED_REFERENCES = FieldOneToMany.create(type, Reference.OWNER, 0); DELETION_DETECTOR = FieldManyToOne.create(type, AllObjects.BASE_CLASS_INSTANCES); type.useStandardRefCounting().done(); } public BaseClass(Nd nd, AllObjects deletionDetector) { super(nd); DELETION_DETECTOR.put(nd, this.address, deletionDetector); } public BaseClass(Nd nd, long record) { super(nd, record); } } public static class SubClass extends BaseClass { public static final FieldOneToMany<Reference> MORE_REFERENCES; @SuppressWarnings("hiding") public static final StructDef<SubClass> type; static { type = StructDef.create(SubClass.class, BaseClass.type); MORE_REFERENCES = FieldOneToMany.create(type, Reference.SUB_CLASS_REFERENCE, 0); type.useStandardRefCounting().done(); } public SubClass(Nd nd, long record) { super(nd, record); } public SubClass(Nd nd, AllObjects deletionDetector) { super(nd, deletionDetector); } } public static class Reference extends NdNode { public static final FieldManyToOne<BaseClass> BASE_CLASS_REFERENCE; public static final FieldManyToOne<BaseClass> OWNER; public static final FieldManyToOne<SubClass> SUB_CLASS_REFERENCE; public static final FieldManyToOne<AllObjects> DELETION_DETECTOR; @SuppressWarnings("hiding") public static StructDef<Reference> type; static { type = StructDef.create(Reference.class, NdNode.type); BASE_CLASS_REFERENCE = FieldManyToOne.create(type, BaseClass.INCOMING_REFERENCES); OWNER = FieldManyToOne.createOwner(type, BaseClass.OWNED_REFERENCES); SUB_CLASS_REFERENCE = FieldManyToOne.create(type, SubClass.MORE_REFERENCES); DELETION_DETECTOR = FieldManyToOne.create(type, AllObjects.REFERENCE_INSTANCES); type.done(); } public Reference(Nd nd, long record) { super(nd, record); } public Reference(Nd nd, AllObjects deletionDetector) { super(nd); DELETION_DETECTOR.put(nd, this.address, deletionDetector); } public void setBaseClassReference(BaseClass target) { BASE_CLASS_REFERENCE.put(getNd(), this.address, target); } public void setOwner(BaseClass target) { OWNER.put(getNd(), this.address, target); } public void setSubClassReference(SubClass target) { SUB_CLASS_REFERENCE.put(getNd(), this.address, target); } } AllObjects allObjects; BaseClass baseClass; SubClass subClass; Reference refA; Reference refB; Reference refC; private Nd nd; @Override protected void setUp() throws Exception { super.setUp(); NdNodeTypeRegistry<NdNode> registry = new NdNodeTypeRegistry<>(); registry.register(0, BaseClass.type.getFactory()); registry.register(1, SubClass.type.getFactory()); registry.register(2, Reference.type.getFactory()); registry.register(3, AllObjects.type.getFactory()); this.nd = DatabaseTestUtil.createEmptyNd(getName(), registry); this.nd.getDB().setExclusiveLock(); this.allObjects = new AllObjects(this.nd); this.baseClass = new BaseClass(this.nd, this.allObjects); this.subClass = new SubClass(this.nd, this.allObjects); this.refA = new Reference(this.nd, this.allObjects); this.refB = new Reference(this.nd, this.allObjects); this.refC = new Reference(this.nd, this.allObjects); } public static Test suite() { return BaseTestCase.suite(InheritenceTests.class); } public void testRemovingOnlyRefcountDeletesObject() { assertTrue(this.allObjects.contains(this.subClass)); this.refA.setSubClassReference(this.subClass); this.refA.setSubClassReference(null); this.nd.processDeletions(); assertFalse(this.allObjects.contains(this.subClass)); } public void testReferencesToBaseClassIncludedInRefCountA() { // Test what happens when the subclass reference is removed first. this.refA.setSubClassReference(this.subClass); this.refB.setBaseClassReference(this.subClass); assertTrue(this.allObjects.contains(this.subClass)); this.refA.setSubClassReference(null); this.nd.processDeletions(); assertTrue(this.allObjects.contains(this.subClass)); this.refB.setBaseClassReference(null); this.nd.processDeletions(); assertFalse(this.allObjects.contains(this.subClass)); } public void testReferencesToBaseClassIncludedInRefCountB() { // Test what happens when the base class reference is removed first. this.refA.setSubClassReference(this.subClass); this.refB.setBaseClassReference(this.subClass); this.nd.processDeletions(); assertTrue(this.allObjects.contains(this.subClass)); this.refB.setBaseClassReference(null); this.nd.processDeletions(); assertTrue(this.allObjects.contains(this.subClass)); this.refA.setSubClassReference(null); this.nd.processDeletions(); assertFalse(this.allObjects.contains(this.subClass)); } public void testOwnedPointersDontCountTowardsRefCount() { this.refA.setOwner(this.subClass); this.nd.processDeletions(); assertTrue(this.allObjects.contains(this.subClass)); this.refB.setBaseClassReference(this.subClass); this.nd.processDeletions(); assertTrue(this.allObjects.contains(this.subClass)); assertTrue(this.allObjects.contains(this.refA)); this.refB.setBaseClassReference(null); this.nd.processDeletions(); assertFalse(this.allObjects.contains(this.subClass)); assertFalse(this.allObjects.contains(this.refA)); } public void testMultipleReferences() { this.refA.setBaseClassReference(this.subClass); this.refB.setBaseClassReference(this.subClass); this.refA.setBaseClassReference(null); this.nd.processDeletions(); assertTrue(this.allObjects.contains(this.subClass)); this.refB.setBaseClassReference(null); this.nd.processDeletions(); assertFalse(this.allObjects.contains(this.subClass)); } }