| /* |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You 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.File; |
| import java.io.FileNotFoundException; |
| import java.io.FileOutputStream; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.PrintStream; |
| |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Modifier; |
| |
| import java.security.AccessController; |
| import java.security.PrivilegedAction; |
| |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Properties; |
| import java.util.StringTokenizer; |
| import java.util.Vector; |
| |
| import javax.jdo.Extent; |
| import javax.jdo.JDOException; |
| import javax.jdo.JDOFatalException; |
| import javax.jdo.JDOHelper; |
| import javax.jdo.JDOObjectNotFoundException; |
| import javax.jdo.PersistenceManager; |
| import javax.jdo.PersistenceManagerFactory; |
| import javax.jdo.Query; |
| |
| import junit.framework.AssertionFailedError; |
| 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 PERSISTENT_NONTRANSACTIONAL_DIRTY = 10; |
| public static final int DETACHED_CLEAN = 11; |
| public static final int DETACHED_DIRTY = 12; |
| public static final int NUM_STATES = 13; |
| public static final int ILLEGAL_STATE = 13; |
| |
| public static final String[] states = { |
| "transient", |
| "persistent-new", |
| "persistent-clean", |
| "persistent-dirty", |
| "hollow", |
| "transient-clean", |
| "transient-dirty", |
| "persistent-new-deleted", |
| "persistent-deleted", |
| "persistent-nontransactional", |
| "persistent-nontransactional-dirty", |
| "detached-clean", |
| "detached-dirty", |
| "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 IS_DETACHED = 5; |
| private static final int NUM_STATUSES = 6; |
| |
| /* |
| * 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 IS_DETACHED |
| // transient |
| { false, false, false, false, false, false}, |
| |
| // persistent-new |
| { true, true, true, true, false, false}, |
| |
| // persistent-clean |
| { true, true, false, false, false, false}, |
| |
| // persistent-dirty |
| { true, true, true, false, false, false}, |
| |
| // hollow |
| { true, false, false, false, false, false}, |
| |
| // transient-clean |
| { false, true, false, false, false, false}, |
| |
| // transient-dirty |
| { false, true, true, false, false, false}, |
| |
| // persistent-new-deleted |
| { true, true, true, true, true, false}, |
| |
| // persistent-deleted |
| { true, true, true, false, true, false}, |
| |
| // persistent-nontransactional |
| { true, false, false, false, false, false}, |
| |
| // persistent-nontransactional-dirty |
| { true, false, true, false, false, false}, |
| |
| // detached_clean |
| { false, false, false, false, false, true}, |
| |
| // detached_dirty |
| { false, false, true, false, false, true} |
| }; |
| |
| /** Name of the PersistenceManagerFactoryClass PMF property. */ |
| public static final String PMF_CLASS_PROP = |
| "javax.jdo.PersistenceManagerFactoryClass"; |
| |
| /** Name of the ConnectionURL PMF property. */ |
| public static final String CONNECTION_URL_PROP = |
| "javax.jdo.option.ConnectionURL"; |
| |
| /** Name of the ConnectionUserName PMF property. */ |
| public static final String CONNECTION_USERNAME_PROP = |
| "javax.jdo.option.ConnectionUserName"; |
| |
| /** Name of the ConnectionPassword PMF property. */ |
| public static final String CONNECTION_PASSWORD_PROP = |
| "javax.jdo.option.ConnectionPassword"; |
| |
| /** identitytype value for applicationidentity. */ |
| public static final String APPLICATION_IDENTITY = "applicationidentity"; |
| |
| /** identitytype value for datastoreidentity. */ |
| public static final String DATASTORE_IDENTITY = "datastoreidentity"; |
| |
| /** |
| * String indicating the type of identity used for the current test case. |
| * The value is either "applicationidentity" or "datastoreidentity". |
| */ |
| protected final String identitytype = System.getProperty("jdo.tck.identitytype"); |
| |
| /** |
| * String indicating the name of the schema for the current test. |
| */ |
| protected final String schemaname = System.getProperty("jdo.tck.schemaname"); |
| |
| /** Name of the file containing the properties for the PMF. */ |
| protected static String PMFProperties = System.getProperty("PMFProperties"); |
| |
| /** Flag indicating whether to clean up data after tests or not. |
| * If false then test will not clean up data from database. |
| * The default value is true. |
| */ |
| protected static boolean cleanupData = |
| System.getProperty("jdo.tck.cleanupaftertest", "true").equalsIgnoreCase("true"); |
| |
| /** Flag indicating whether to close the PMF after each test or not. |
| * It defaults to false. |
| */ |
| protected static final boolean closePMFAfterEachTest = |
| System.getProperty("jdo.tck.closePMFAfterEachTest", "false").equalsIgnoreCase("true"); |
| |
| /** The Properties object for the PersistenceManagerFactory. */ |
| protected static Properties PMFPropertiesObject; |
| |
| /** The PersistenceManagerFactory. */ |
| protected static PersistenceManagerFactory pmf; |
| |
| /** The collection of supported options of the pmf. */ |
| private static Collection supportedOptions; |
| |
| /** The name of the pmf supported options summary file. */ |
| private static final String PMF_SUPPORTED_OPTIONS_FILE_NAME = "pmf_supported_options.txt"; |
| |
| /** 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(); |
| |
| /** |
| * Indicates an exception thrown in method <code>tearDown</code>. |
| * At the end of method <code>tearDown</code> this field is nullified. |
| */ |
| private Throwable tearDownThrowable; |
| |
| /** |
| * A list of registered oid instances. |
| * Corresponding pc instances are deleted in <code>localTearDown</code>. |
| */ |
| private Collection tearDownInstances = new LinkedList(); |
| |
| /** |
| * A list of registered pc classes. |
| * The extents of these classes are deleted in <code>localTearDown</code>. |
| */ |
| private Collection tearDownClasses = new LinkedList(); |
| |
| /** */ |
| protected final void setUp() throws Exception { |
| pmf = getPMF(); |
| localSetUp(); |
| } |
| |
| /** |
| * Subclasses may override this method to allocate any data and resources |
| * that they need in order to successfully execute this testcase. |
| */ |
| protected void localSetUp() {} |
| |
| /** |
| * Runs the bare test sequence. |
| * @exception Throwable if any exception is thrown |
| */ |
| public final void runBare() throws Throwable { |
| try { |
| testSucceeded = false; |
| setUp(); |
| runTest(); |
| testSucceeded = true; |
| } |
| catch (AssertionFailedError e) { |
| if (logger.isInfoEnabled()) |
| logger.info("Exception during setUp or runtest: ", e); |
| throw e; |
| } |
| catch (Throwable t) { |
| if (logger.isInfoEnabled()) |
| logger.info("Exception during setUp or runtest: ", t); |
| throw t; |
| } |
| finally { |
| tearDown(); |
| if (debug) { |
| logger.debug("Free memory: " + Runtime.getRuntime().freeMemory()); |
| } |
| } |
| } |
| |
| /** |
| * Sets field <code>tearDownThrowable</code> if it is <code>null</code>. |
| * Else, the given throwable is logged using fatal log level. |
| * @param throwable the throwable |
| */ |
| private void setTearDownThrowable(String context, Throwable throwable) |
| { |
| if (logger.isInfoEnabled()) |
| logger.info("Exception during "+context+": ", throwable); |
| if (this.tearDownThrowable == null) { |
| this.tearDownThrowable = throwable; |
| } |
| } |
| |
| /** |
| * This method clears data and resources allocated by testcases. |
| * It first closes the persistence manager of this testcase. |
| * Then it calls method <code>localTearDown</code>. |
| * Subclasses may override that method to clear any data and resources |
| * that they have allocated in method <code>localSetUp</code>. |
| * Finally, this method closes the persistence manager factory.<p> |
| * |
| * <b>Note:</b>These methods are called always, regardless of any exceptions. |
| * The first caught exception is kept in field <code>tearDownThrowable</code>. |
| * That exception is thrown as a nested exception of <code>JDOFatalException</code> |
| * if and only if the testcase executed successful. |
| * Otherwise that exception is logged using fatal log level. |
| * All other exceptions are logged using fatal log level, always.<p> |
| * |
| * <b>Note:</b>By default, the method tearDown does not close the pmf. |
| * This is done at the end of each configuration, unless the property |
| * jdo.tck.closePMFAfterEachTest is set to true. |
| */ |
| protected final void tearDown() { |
| try { |
| cleanupPM(); |
| } |
| catch (Throwable t) { |
| setTearDownThrowable("cleanupPM", t); |
| } |
| |
| if ((pmf == null || pmf.isClosed()) && |
| (this.tearDownInstances.size() > 0 || this.tearDownClasses.size() > 0)) |
| throw new JDOFatalException ("PMF must not be nullified or closed when tear down instances and /or classes have been added."); |
| |
| if (pmf != null && pmf.isClosed()) |
| pmf = null; |
| |
| try { |
| if (cleanupData) { |
| localTearDown(); |
| } |
| } |
| catch (Throwable t) { |
| setTearDownThrowable("localTearDown", t); |
| } |
| |
| if (closePMFAfterEachTest) { |
| try { |
| closePMF(); |
| } |
| catch (Throwable t) { |
| setTearDownThrowable("closePMF", t); |
| } |
| } |
| |
| if (this.tearDownThrowable != null) { |
| Throwable t = this.tearDownThrowable; |
| this.tearDownThrowable = null; |
| if (testSucceeded) { |
| // runTest succeeded, but this method threw exception => error |
| throw new JDOFatalException("Exception during tearDown", t); |
| } |
| } |
| } |
| |
| /** |
| * Deletes all registered pc instances and extents of all registered pc classes. |
| * Subclasses may override this method to clear any data and resources |
| * that they have allocated in method <code>localSetUp</code>. |
| */ |
| protected void localTearDown() { |
| deleteTearDownInstances(); |
| deleteTearDownClasses(); |
| } |
| |
| protected void addTearDownObjectId(Object oid) { |
| // ensure that oid is not a PC instance |
| if (JDOHelper.getObjectId(oid) != null || |
| JDOHelper.isTransactional(oid)) |
| throw new IllegalArgumentException("oid"); |
| this.tearDownInstances.add(oid); |
| } |
| |
| protected void addTearDownInstance(Object pc) { |
| Object oid = JDOHelper.getObjectId(pc); |
| addTearDownObjectId(oid); |
| } |
| |
| protected void addTearDownClass(Class pcClass) { |
| this.tearDownClasses.add(pcClass); |
| } |
| |
| protected void addTearDownClass(Class[] pcClasses) { |
| if (pcClasses == null) return; |
| for (int i = 0; i < pcClasses.length; ++i) { |
| addTearDownClass(pcClasses[i]); |
| } |
| } |
| |
| /** |
| * Deletes and removes tear down instances. |
| * If there are no tear down instances, |
| * the this method is a noop. |
| * Otherwise, tear down instances are deleted |
| * exactly in the order they have been added. |
| * Tear down instances are deleted in a separate transaction. |
| */ |
| protected void deleteTearDownInstances() { |
| if (this.tearDownInstances.size() > 0) { |
| getPM(); |
| try { |
| this.pm.currentTransaction().begin(); |
| for (Iterator i = this.tearDownInstances.iterator(); i.hasNext(); ) { |
| Object pc; |
| try { |
| pc = this.pm.getObjectById(i.next(), true); |
| } |
| catch (JDOObjectNotFoundException e) { |
| pc = null; |
| } |
| // we only delete those persistent instances |
| // which have not been deleted by tests already. |
| if (pc != null) { |
| this.pm.deletePersistent(pc); |
| } |
| } |
| this.pm.currentTransaction().commit(); |
| } |
| finally { |
| this.tearDownInstances.clear(); |
| cleanupPM(); |
| } |
| } |
| } |
| |
| /** |
| * Deletes and removes tear down classes. |
| * If there are no tear down classes, |
| * the this method is a noop. |
| * Otherwise, tear down classes are deleted |
| * exactly in the order they have been added. |
| * Tear down classes are deleted in a separate transaction. |
| * Deleting a tear down class means to delete the extent. |
| */ |
| protected void deleteTearDownClasses() { |
| if (this.tearDownClasses.size() > 0) { |
| getPM(); |
| try { |
| this.pm.currentTransaction().begin(); |
| for (Iterator i = this.tearDownClasses.iterator(); i.hasNext(); ) { |
| this.pm.deletePersistentAll(getAllObjects(this.pm, (Class)i.next())); |
| } |
| this.pm.currentTransaction().commit(); |
| } |
| finally { |
| this.tearDownClasses.clear(); |
| cleanupPM(); |
| } |
| } |
| } |
| |
| /** */ |
| protected Collection getAllObjects(PersistenceManager pm, Class pcClass) { |
| Collection col = new Vector() ; |
| Query query = pm.newQuery(); |
| query.setClass(pcClass); |
| Extent candidates = null; |
| try { |
| candidates = pm.getExtent(pcClass, false); |
| } catch (JDOException ex) { |
| if (debug) logger.debug("Exception thrown for getExtent of class " + |
| pcClass.getName()); |
| return col; |
| } |
| query.setCandidates(candidates); |
| Object result = query.execute(); |
| return (Collection)result; |
| } |
| |
| /** |
| * Get the <code>PersistenceManagerFactory</code> instance |
| * for the implementation under test. |
| * @return field <code>pmf</code> if it is not <code>null</code>, |
| * else sets field <code>pmf</code> to a new instance and returns that instance. |
| */ |
| protected PersistenceManagerFactory getPMF() |
| { |
| if (pmf == null) { |
| PMFPropertiesObject = loadProperties(PMFProperties); // will exit here if no properties |
| pmf = JDOHelper.getPersistenceManagerFactory(PMFPropertiesObject); |
| if (supportedOptions == null) { |
| supportedOptions = pmf.supportedOptions(); |
| } |
| } |
| return pmf; |
| } |
| |
| /** |
| * Get the <code>PersistenceManagerFactory</code> instance |
| * for the implementation under test. This method does NOT use the |
| * JDOHelper method to retrieve the PMF, instead it creates an instance of |
| * the class specified as javax.jdo.PersistenceManagerFactoryClass |
| * property. The returned PMF is not configured. |
| * @return field <code>pmf</code> if it is not <code>null</code>, |
| * else sets field <code>pmf</code> to a new instance and returns that instance. |
| */ |
| protected PersistenceManagerFactory getUnconfiguredPMF() |
| { |
| if (pmf == null) { |
| PMFPropertiesObject = loadProperties(PMFProperties); // will exit here if no properties |
| String name = PMFPropertiesObject.getProperty(PMF_CLASS_PROP); |
| try { |
| Class pmfClass = Class.forName(name); |
| pmf = (PersistenceManagerFactory) pmfClass.newInstance(); |
| if (supportedOptions == null) { |
| supportedOptions = pmf.supportedOptions(); |
| } |
| } catch (ClassNotFoundException ex) { |
| throw new JDOException("Cannot find PMF class '" + name + "'.", |
| ex); |
| } catch (InstantiationException ex) { |
| throw new JDOException("Cannot instantiate PMF class '" + |
| name + "'.", ex); |
| } catch (IllegalAccessException ex) { |
| throw new JDOException("Cannot access PMF class '" + name + |
| "' or its no-arg constructor.", ex); |
| } |
| } |
| 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 cleanupPM() |
| { |
| 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 static 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. */ |
| public static void closePMF() { |
| JDOException failure = null; |
| while (pmf != null) { |
| try { |
| if (!pmf.isClosed()) |
| 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 static 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 { |
| throw new JDOFatalException(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 + NL + msg); |
| } |
| |
| // Helper methods to check for supported options |
| |
| /** |
| * Dump the supportedOptions to the a file in the specified directory. |
| */ |
| public static void dumpSupportedOptions(String directory) { |
| if (supportedOptions == null) |
| return; |
| File file = new File(directory, PMF_SUPPORTED_OPTIONS_FILE_NAME); |
| if (file.exists()) |
| // PMF supported options have been dumped before => return |
| return; |
| PrintStream resultStream = null; |
| try { |
| resultStream = new PrintStream(new FileOutputStream(file)); |
| for (Iterator it = supportedOptions.iterator(); it.hasNext();) { |
| resultStream.println((String)it.next()); |
| } |
| } catch (FileNotFoundException e) { |
| throw new JDOFatalException( |
| "dumpSupportedOptions: cannot create file " + file.getName(), e); |
| } finally { |
| if (resultStream != null) |
| resultStream.close(); |
| } |
| } |
| |
| /** |
| * 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.NonDurableIdentity"); |
| } |
| |
| /** 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"); |
| } |
| |
| /** Reports whether Binary Compatibility is supported. */ |
| public boolean isBinaryCompatibilitySupported() { |
| return getPMF().supportedOptions().contains( |
| "javax.jdo.option.BinaryCompatibility"); |
| } |
| |
| /** Reports whether UnconstrainedVariables is supported. */ |
| public boolean isUnconstrainedVariablesSupported() { |
| return getPMF().supportedOptions().contains( |
| "javax.jdo.option.UnconstrainedVariables"); |
| } |
| |
| /** Reports whether SQL queries are supported. */ |
| public boolean isSQLSupported() { |
| return getPMF().supportedOptions().contains( |
| "javax.jdo.query.SQL"); |
| } |
| |
| /** Reports whether getting the DataStoreConnection is supported. */ |
| public boolean isDataStoreConnectionSupported() { |
| return getPMF().supportedOptions().contains( |
| "javax.jdo.option.GetDataStoreConnection"); |
| } |
| |
| /** |
| * Determine if a class is loadable in the current environment. |
| */ |
| public static boolean isClassLoadable(String className) { |
| try { |
| Class.forName(className); |
| return true; |
| } catch (ClassNotFoundException ex) { |
| return false; |
| } |
| } |
| |
| /** |
| * Determine if the environment is 1.4 version of JRE or better. |
| */ |
| public static boolean isJRE14orBetter() { |
| return isClassLoadable("java.util.Currency"); |
| } |
| |
| /** |
| * 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"); |
| } |
| if( JDOHelper.isDetached(o) ){ |
| if( existingEntries ) buff.append(", "); |
| buff.append("detached"); |
| } |
| 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[NUM_STATUSES]; |
| 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); |
| status[IS_DETACHED] = JDOHelper.isDetached(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; |
| } |
| |
| /** |
| * Tests if a found state matches an expected state. |
| */ |
| public static boolean compareStates(int found_state, int expected_state) |
| { |
| // status interrogation gives same values for PERSISTENT_NONTRANSACTIONAL and HOLLOW |
| return (expected_state < 0 |
| || found_state == expected_state |
| || (found_state == HOLLOW && expected_state == PERSISTENT_NONTRANSACTIONAL) |
| || (found_state == PERSISTENT_NONTRANSACTIONAL && expected_state == HOLLOW)); |
| } |
| |
| /** This method mangles an object by changing all its non-static, |
| * non-final fields. |
| * It returns true if the object was mangled, and false if there |
| * are no fields to mangle. |
| */ |
| protected boolean mangleObject (Object oid) |
| throws Exception { |
| Field[] fields = getModifiableFields(oid); |
| if (fields.length == 0) return false; |
| for (int i = 0; i < fields.length; ++i) { |
| Field field = fields[i]; |
| Class fieldType = field.getType(); |
| if (fieldType == long.class) { |
| field.setLong(oid, 10000L + field.getLong(oid)); |
| } else if (fieldType == int.class) { |
| field.setInt(oid, 10000 + field.getInt(oid)); |
| } else if (fieldType == short.class) { |
| field.setShort(oid, (short)(10000 + field.getShort(oid))); |
| } else if (fieldType == byte.class) { |
| field.setByte(oid, (byte)(100 + field.getByte(oid))); |
| } else if (fieldType == char.class) { |
| field.setChar(oid, (char)(10 + field.getChar(oid))); |
| } else if (fieldType == String.class) { |
| field.set(oid, "This is certainly a challenge" + (String)field.get(oid)); |
| } else if (fieldType == Integer.class) { |
| field.set(oid, new Integer(10000 + ((Integer)field.get(oid)).intValue())); |
| } else if (fieldType == Long.class) { |
| field.set(oid, new Long(10000L + ((Long)field.get(oid)).longValue())); |
| } else if (fieldType == Short.class) { |
| field.set(oid, new Short((short)(10000 + ((Short)field.get(oid)).shortValue()))); |
| } else if (fieldType == Byte.class) { |
| field.set(oid, new Byte((byte)(100 + ((Byte)field.get(oid)).byteValue()))); |
| } else if (fieldType == Character.class) { |
| field.set(oid, new Character((char)(10 + ((Character)(field.get(oid))).charValue()))); |
| } |
| } |
| return true; |
| } |
| |
| /** Returns modifiable Fields of the class of the parameter. |
| * Fields are considered modifiable if they are not static or final. |
| * This method requires several permissions in order to run with |
| * a SecurityManager, hence the doPrivileged block: |
| * <ul> |
| * <li>ReflectPermission("suppressAccessChecks")</li> |
| * <li>RuntimePermission("accessDeclaredMembers")</li> |
| * </ul> |
| */ |
| protected Field[] getModifiableFields(final Object obj) { |
| return (Field[])AccessController.doPrivileged( |
| new PrivilegedAction () { |
| public Object run () { |
| Class cls = obj.getClass(); |
| List result = new ArrayList(); |
| Field[] fields = cls.getFields(); |
| for (int i = 0; i < fields.length; ++i) { |
| Field field = fields[i]; |
| int modifiers = field.getModifiers(); |
| if (Modifier.isFinal(modifiers) || |
| Modifier.isStatic(modifiers)) |
| continue; |
| field.setAccessible(true); |
| result.add(field); |
| } |
| return result.toArray(new Field[result.size()]); |
| } |
| } |
| ); |
| } |
| |
| /** |
| * Returns <code>true</code> if the current test runs with application |
| * identity. This means the system property jdo.tck.identitytype has the |
| * value applicationidentity. |
| * @return <code>true</code> if current test runs with application |
| * identity; <code>false</code> otherwise: |
| */ |
| public boolean runsWithApplicationIdentity() { |
| return APPLICATION_IDENTITY.equals(identitytype); |
| } |
| |
| /** |
| * Prints a message (if debug is true) saying the test with the |
| * specified name is not executed, because the JDO implementation under |
| * test is run for an inapplicable identity type. |
| * @param testName the name of the test method that is skipped. |
| * @param requiredIdentityType the name of the required identity type. |
| */ |
| protected void printNonApplicableIdentityType( |
| String testName, String requiredIdentityType) { |
| if (debug) { |
| logger.debug( |
| "Test " + testName + |
| " was not run, because it is only applicable for identity type " + |
| requiredIdentityType + |
| ". The identity type of the current configuration is " + |
| identitytype); |
| } |
| } |
| |
| /** |
| * Returns the value of the PMF property |
| * given by argument <code>key</code>. |
| * @param key the key |
| * @return the value |
| */ |
| protected String getPMFProperty(String key) { |
| return PMFPropertiesObject.getProperty(key); |
| } |
| |
| /** |
| * Returns <code>true</code> if the implementation under test |
| * supports all JDO options contained in system property |
| * <code>jdo.tck.requiredOptions</code>. |
| * @return <code>true</code> if the implementation under test |
| * supports all JDO options contained in system property |
| * <code>jdo.tck.requiredOptions</code> |
| */ |
| protected boolean isTestToBePerformed() { |
| boolean isTestToBePerformed = true; |
| String requiredOptions = System.getProperty("jdo.tck.requiredOptions"); |
| Collection supportedOptions = getPMF().supportedOptions(); |
| StringTokenizer tokenizer = new StringTokenizer( |
| requiredOptions, " ,;\n\r\t"); |
| while (tokenizer.hasMoreTokens()) { |
| String requiredOption = tokenizer.nextToken(); |
| logger.debug("Required option: " + requiredOption); |
| if (!requiredOption.equals("") && |
| !supportedOptions.contains(requiredOption)) { |
| isTestToBePerformed = false; |
| printUnsupportedOptionalFeatureNotTested( |
| getClass().getName(), requiredOption); |
| } |
| } |
| return isTestToBePerformed; |
| } |
| |
| /** New line. |
| */ |
| public static final String NL = System.getProperty("line.separator"); |
| |
| /** A buffer of of error messages. |
| */ |
| protected static StringBuffer messages; |
| |
| /** Appends to error messages. |
| */ |
| protected static synchronized void appendMessage(String message) { |
| if (messages == null) { |
| messages = new StringBuffer(NL); |
| } |
| messages.append(message); |
| messages.append(NL); |
| } |
| |
| /** Appends to error messages. |
| */ |
| protected static synchronized void deferredAssertTrue(boolean test, |
| String context, String message) { |
| if (!test) { |
| appendMessage(context + ": " +message); |
| } |
| } |
| |
| /** |
| * Returns collected error messages, or <code>null</code> if there |
| * are none, and clears the buffer. |
| */ |
| protected static synchronized String retrieveMessages() { |
| if (messages == null) { |
| return null; |
| } |
| final String msg = messages.toString(); |
| messages = null; |
| return msg; |
| } |
| |
| /** |
| * Fail the test if there are any error messages. |
| */ |
| protected void failOnError() { |
| String errors = retrieveMessages(); |
| if (errors != null) { |
| fail (errors); |
| } |
| } |
| } |