blob: 7e758eba91715696c6b8b39c8b6b2ee8f835d296 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015, 2016 Google, Inc and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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));
}
}