blob: faabef906ddffe56801fa4b34636732f1829e8ac [file] [log] [blame]
/*
* Copyright 2005 The Apache Software Foundation.
*
* 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.apache.jdo.tck;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Properties;
import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.JDOException;
import junit.framework.TestCase;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public abstract class JDO_Test extends TestCase {
public static final int TRANSIENT = 0;
public static final int PERSISTENT_NEW = 1;
public static final int PERSISTENT_CLEAN = 2;
public static final int PERSISTENT_DIRTY = 3;
public static final int HOLLOW = 4;
public static final int TRANSIENT_CLEAN = 5;
public static final int TRANSIENT_DIRTY = 6;
public static final int PERSISTENT_NEW_DELETED = 7;
public static final int PERSISTENT_DELETED = 8;
public static final int PERSISTENT_NONTRANSACTIONAL = 9;
public static final int NUM_STATES = 10;
public static final int ILLEGAL_STATE = 10;
public static final String[] states = {
"transient",
"persistent-new",
"persistent-clean",
"persistent-dirty",
"hollow",
"transient-clean",
"transient-dirty",
"persistent-new-deleted",
"persistent-deleted",
"persistent-nontransactional",
"illegal"
};
private static final int IS_PERSISTENT = 0;
private static final int IS_TRANSACTIONAL = 1;
private static final int IS_DIRTY = 2;
private static final int IS_NEW = 3;
private static final int IS_DELETED = 4;
private static final int NUM_STATUSES = 5;
/*
* This table indicates the values returned by the status interrogation
* methods for each state. This is used to determine the current lifecycle
* state of an object.
*/
private static final boolean state_statuses[][] = {
// IS_PERSISTENT IS_TRANSACTIONAL IS_DIRTY IS_NEW IS_DELETED
// transient
{ false, false, false, false, false},
// persistent-new
{ true, true, true, true, false},
// persistent-clean
{ true, true, false, false, false},
// persistent-dirty
{ true, true, true, false, false},
// hollow
{ true, false, false, false, false},
// transient-clean
{ false, true, false, false, false},
// transient-dirty
{ false, true, true, false, false},
// persistent-new-deleted
{ true, true, true, true, true},
// persistent-deleted
{ true, true, true, false, true},
// persistent-nontransactional
{ true, false, false, false, false}
};
/** Name of the file contaninig the properties for the PMF. */
protected static String PMFProperties;
/** The Properties object for the PersistenceManagerFactory. */
protected static Properties PMFPropertiesObject;
/** The PersistenceManagerFactory. */
protected PersistenceManagerFactory pmf;
/** The PersistenceManager. */
protected PersistenceManager pm;
// Flag indicating successful test run
protected boolean testSucceeded;
/** Logger */
protected Log logger =
LogFactory.getFactory().getInstance("org.apache.jdo.tck");
/** true if debug logging in enabled. */
protected boolean debug = logger.isDebugEnabled();
/** */
protected JDO_Test() {
PMFProperties = System.getProperty("PMFProperties");
}
/** */
protected void setUp() throws Exception {
pmf = getPMF();
}
/**
* Runs the bare test sequence.
* @exception Throwable if any exception is thrown
*/
public void runBare() throws Throwable {
setUp();
try {
testSucceeded = false;
runTest();
testSucceeded = true;
}
finally {
tearDown();
}
}
/** */
protected void tearDown() {
try {
cleanup();
closePMF();
}
catch (Throwable ex) {
if (debug) ex.printStackTrace();
if (testSucceeded) {
// runTest succeeded, but closePMF throws exception =>
// failure
fail("Exception during tearDown: " + ex);
}
else {
// runTest failed and closePMF throws exception =>
// just print the closePMF exception, otherwise the
// closePMF exception would swallow the test case failure
if (debug) logger.debug("Exception during tearDown: " + ex);
}
}
}
/**
* Get the <code>PersistenceManagerFactory</code> instance
* for the implementation under test.
*/
protected PersistenceManagerFactory getPMF()
{
if (pmf == null) {
PMFPropertiesObject = loadProperties(PMFProperties); // will exit here if no properties
pmf = JDOHelper.getPersistenceManagerFactory(PMFPropertiesObject);
}
return pmf;
}
/**
* Get the <code>PersistenceManager</code> instance
* for the implementation under test.
*/
protected PersistenceManager getPM() {
if (pm == null) {
pm = getPMF().getPersistenceManager();
}
return pm;
}
/**
* This method cleans up the environment: closes the
* <code>PersistenceManager</code>. This should avoid leaving
* multiple PersistenceManager instances around, in case the
* PersistenceManagerFactory performs PersistenceManager pooling.
*/
protected void cleanup()
{
cleanupPM(pm);
pm = null;
}
/**
* This method cleans up the specified
* <code>PersistenceManager</code>. If the pm still has an open
* transaction, it will be rolled back, before closing the pm.
*/
protected void cleanupPM(PersistenceManager pm)
{
if ((pm != null) && !pm.isClosed()) {
if (pm.currentTransaction().isActive()) {
pm.currentTransaction().rollback();
}
pm.close();
}
}
/** Closes the pmf stored in this instance. */
protected void closePMF()
{
JDOException failure = null;
while (pmf != null) {
try {
pmf.close();
pmf = null;
}
catch (JDOException ex) {
// store failure of first call pmf.close
if (failure == null)
failure = ex;
PersistenceManager[] pms = getFailedPersistenceManagers(
"closePMF", ex);
for (int i = 0; i < pms.length; i++) {
cleanupPM(pms[i]);
}
}
}
// rethrow JDOException thrown by pmf.close
if (failure != null)
throw failure;
}
/** */
protected PersistenceManager[] getFailedPersistenceManagers(
String assertionFailure, JDOException ex) {
Throwable[] nesteds = ex.getNestedExceptions();
int numberOfExceptions = nesteds==null ? 0 : nesteds.length;
PersistenceManager[] result = new PersistenceManager[numberOfExceptions];
for (int i = 0; i < numberOfExceptions; ++i) {
JDOException exc = (JDOException)nesteds[i];
Object failedObject = exc.getFailedObject();
if (exc.getFailedObject() instanceof PersistenceManager) {
result[i] = (PersistenceManager)failedObject;
} else {
fail(assertionFailure,
"Unexpected failed object of type: " +
failedObject.getClass().getName());
}
}
return result;
}
/**
* This method load Properties from a given file.
*/
protected Properties loadProperties(String fileName)
{
if (fileName == null) {
fileName = System.getProperty("user.home") + "/.jdo/PMFProperties.properties";
}
Properties props = new Properties();
InputStream propStream = null;
try {
propStream = new FileInputStream(fileName);
}
catch (IOException ex) {
System.out.println("Could not open properties file \"" + fileName + "\"");
System.out.println("Please specify a system property PMFProperties " +
"with the PMF properties file name as value " +
"(defaults to {user.home}/.jdo/PMFProperties.properties)");
System.exit(1);
}
try {
props.load(propStream);
}
catch (IOException ex) {
System.out.println("Error loading properties file \"" + fileName + "\"");
ex.printStackTrace();
System.exit(1);
}
return props;
}
/**
* Prints the specified msg (if debug is true), before it aborts the
* test case.
*/
public void fail(String assertionFailure, String msg) {
if (debug) logger.debug(msg);
fail(assertionFailure + "\n" + msg);
}
// Helper methods to check for supported options
/**
* Prints a message (if debug is true) saying the test with the
* specified name is not executed, because the JDO implementation under
* test does not support the specified optional feature.
* @param testName the name of the test method that is skipped.
* @param optionalFeature the name of the option not supported by the
* JDO implementation under tets.
*/
protected void printUnsupportedOptionalFeatureNotTested(
String testName, String optionalFeature) {
if (debug) {
logger.debug(
"Test " + testName +
" was not run, because optional feature " + optionalFeature +
" is not supported by the JDO implementation under test");
}
}
/** Reports whether TransientTransactional is supported. */
public boolean isTransientTransactionalSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.TransientTransactional");
}
/** Reports whether NontransactionalRead is supported. */
public boolean isNontransactionalReadSupported(){
return getPMF().supportedOptions().contains(
"javax.jdo.option.NontransactionalRead");
}
/** Reports whether NontransactionalWrite is supported. */
public boolean isNontransactionalWriteSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.NontransactionalWrite");
}
/** Reports whether RetainValues is supported. */
public boolean isRetainValuesSupported()
{
return getPMF().supportedOptions().contains(
"javax.jdo.option.RetainValues");
}
/** Reports whether Optimistic is supported. */
public boolean isOptimisticSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.Optimistic");
}
/** Reports whether Application Identity is supported. */
public boolean isApplicationIdentitySupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.ApplicationIdentity");
}
/** Reports whether Datastore Identity is supported. */
public boolean isDatastoreIdentitySupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.DatastoreIdentity");
}
/** Reports whether Non-Durable Identity is supported. */
public boolean isNonDurableIdentitySupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.NonDatastoreIdentity");
}
/** Reports whether an <code>ArrayList</code> collection is supported. */
public boolean isArrayListSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.ArrayList");
}
/** Reports whether a <code>HashMap</code> collection is supported. */
public boolean isHashMapSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.HashMap");
}
/** Reports whether a <code>Hashtable</code> collection is supported. */
public boolean isHashtableSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.Hashtable");
}
/** Reports whether a <code>LinkedList</code> collection is supported. */
public boolean isLinkedListSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.LinkedList");
}
/** Reports whether a <code>TreeMap</code> collection is supported. */
public boolean isTreeMapSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.TreeMap");
}
/** Reports whether a <code>TreeSet</code> collection is supported. */
public boolean isTreeSetSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.TreeSet");
}
/** Reports whether a <code>Vector</code> collection is supported. */
public boolean isVectorSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.Vector");
}
/** Reports whether a <code>Map</code> collection is supported. */
public boolean isMapSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.Map");
}
/** Reports whether a <code>List</code> collection is supported. */
public boolean isListSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.List");
}
/** Reports whether arrays are supported. */
public boolean isArraySupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.Array");
}
/** Reports whether a null collection is supported. */
public boolean isNullCollectionSupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.NullCollection");
}
/** Reports whether Changing Application Identity is supported. */
public boolean isChangeApplicationIdentitySupported() {
return getPMF().supportedOptions().contains(
"javax.jdo.option.ChangeApplicationIdentity");
}
/**
* This utility method returns a <code>String</code> that indicates the
* current state of an instance.
* @param o The object.
* @return The current state of the instance, by using the
* <code>JDOHelper</code> state interrogation methods.
*/
public static String getStateOfInstance(Object o)
{
boolean existingEntries = false;
StringBuffer buff = new StringBuffer("{");
if( JDOHelper.isPersistent(o) ){
buff.append("persistent");
existingEntries = true;
}
if( JDOHelper.isTransactional(o) ){
if( existingEntries ) buff.append(", ");
buff.append("transactional");
existingEntries = true;
}
if( JDOHelper.isDirty(o) ){
if( existingEntries ) buff.append(", ");
buff.append("dirty");
existingEntries = true;
}
if( JDOHelper.isNew(o) ){
if( existingEntries ) buff.append(", ");
buff.append("new");
existingEntries = true;
}
if( JDOHelper.isDeleted(o) ){
if( existingEntries ) buff.append(", ");
buff.append("deleted");
}
buff.append("}");
return buff.toString();
}
/**
* This method will return the current lifecycle state of an instance.
*/
public static int currentState(Object o)
{
boolean[] status = new boolean[5];
status[IS_PERSISTENT] = JDOHelper.isPersistent(o);
status[IS_TRANSACTIONAL] = JDOHelper.isTransactional(o);
status[IS_DIRTY] = JDOHelper.isDirty(o);
status[IS_NEW] = JDOHelper.isNew(o);
status[IS_DELETED] = JDOHelper.isDeleted(o);
int i, j;
outerloop:
for( i = 0; i < NUM_STATES; ++i ){
for( j = 0; j < NUM_STATUSES; ++j ){
if( status[j] != state_statuses[i][j] )
continue outerloop;
}
return i;
}
return NUM_STATES;
}
/** This method mangles an object by changing all its public fields
*/
protected void mangleObject (Object oid)
throws Exception {
Class oidClass = oid.getClass();
Field[] fields = oidClass.getFields();
for (int i = 0; i < fields.length; ++i) {
Field field = fields[i];
int modifiers = field.getModifiers();
if (java.lang.reflect.Modifier.isFinal(modifiers) ||
java.lang.reflect.Modifier.isStatic(modifiers))
break;
field.setAccessible(true);
if (debug)
logger.debug("field" + i + " has name: " + field.getName() +
" type: " + field.getType());
Class fieldType = field.getType();
if (fieldType == long.class) {
field.setLong(oid, 10000L + field.getLong(oid));
}
if (fieldType == int.class) {
field.setInt(oid, 10000 + field.getInt(oid));
}
if (fieldType == short.class) {
field.setShort(oid, (short)(10000 + field.getShort(oid)));
}
if (fieldType == byte.class) {
field.setByte(oid, (byte)(100 + field.getByte(oid)));
}
if (fieldType == char.class) {
field.setChar(oid, (char)(10 + field.getChar(oid)));
}
if (fieldType == String.class) {
field.set(oid, "This is certainly a challenge" + (String)field.get(oid));
}
if (fieldType == Integer.class) {
field.set(oid, new Integer(10000 + ((Integer)field.get(oid)).intValue()));
}
if (fieldType == Long.class) {
field.set(oid, new Long(10000L + ((Long)field.get(oid)).longValue()));
}
if (fieldType == Short.class) {
field.set(oid, new Short((short)(10000 + ((Short)field.get(oid)).shortValue())));
}
if (fieldType == Byte.class) {
field.set(oid, new Byte((byte)(100 + ((Byte)field.get(oid)).byteValue())));
}
if (fieldType == Character.class) {
field.set(oid, new Character((char)(10 + ((Character)(field.get(oid))).charValue())));
}
}
}
}