FileDocCategorySizeDatePackage
FunctionalTestCase.javaAPI DocHibernate 3.2.515440Wed Mar 28 11:03:18 BST 2007org.hibernate.junit.functional

FunctionalTestCase

public abstract class FunctionalTestCase extends org.hibernate.junit.UnitTestCase implements ExecutionEnvironment.Settings
Most of the Hibernate test suite in fact is a series of functional tests, not unit tests. Here is a base class for these functional tests.
author
Steve Ebersole

Fields Summary
private static final Log
log
private ExecutionEnvironment
environment
private boolean
isEnvironmentLocallyManaged
private org.hibernate.classic.Session
session
Constructors Summary
public FunctionalTestCase(String string)


	   
		super( string );
	
Methods Summary
public voidafterConfigurationBuilt(org.hibernate.cfg.Mappings mappings, org.hibernate.dialect.Dialect dialect)

	
public voidafterSessionFactoryBuilt(org.hibernate.engine.SessionFactoryImplementor sfi)

	
protected booleanallowsPhysicalColumnNameInHaving(java.lang.String testDescription)
Does the db/dialect support using a column's physical name in the having clause even after it has been aliased in the select/group-by clause. This is not actually required by the SQL spec, although virtually ever DB in the world supports this.

param
testDescription description of the scenario being tested.
return
true if is allowed

		// I only *know* of this being a limitation on Derby, although I highly suspect
		// it is a limitation on any IBM/DB2 variant
		if ( DerbyDialect.class.isInstance( getDialect() ) ) {
			// https://issues.apache.org/jira/browse/DERBY-1624
			reportSkip( "Dialect does not support physical column name in having clause after it is aliased", testDescription );
			return false;
		}
		return true;
	
protected booleanallowsPhysicalColumnNameInOrderby(java.lang.String testDescription)
Does the db/dialect support using a column's physical name in the order-by clause even after it has been aliased in the select clause. This is not actually required by the SQL spec, although virtually ever DB in the world supports this (the most glaring omission here being IBM-variant DBs ala DB2 and Derby).

param
testDescription description of the scenario being tested.
return
true if is allowed

		if ( DB2Dialect.class.isInstance( getDialect() ) ) {
			// https://issues.apache.org/jira/browse/DERBY-1624
			reportSkip( "Dialect does not support physical column name in order-by clause after it is aliased", testDescription );
			return false;
		}
		return true;
	
public booleanappliesTo(org.hibernate.dialect.Dialect dialect)
Intended to indicate that this test class as a whole is intended for a dialect or series of dialects. Skips here (appliesTo = false) therefore simply indicate that the given tests target a particular feature of the checked database and none of the tests on this class should be run for the checked dialect.

param
dialect The dialect to be checked.
return
False if the test class as a whole is specifically targetting a dialect (or series of dialects) other than the indicated dialect and the test should therefore be skipped in its entirety; true otherwise.

		return true;
	
protected voidassertAllDataRemoved()

		if ( !createSchema() ) {
			return; // no tables were created...
		}
		if ( !Boolean.getBoolean( "hibernate.test.validateDataCleanup" ) ) {
			return;
		}

		Session tmpSession = getSessions().openSession();
		try {
			List list = tmpSession.createQuery( "select o from java.lang.Object o" ).list();

			Map items = new HashMap();
			if ( !list.isEmpty() ) {
				for ( Iterator iter = list.iterator(); iter.hasNext(); ) {
					Object element = iter.next();
					Integer l = ( Integer ) items.get( tmpSession.getEntityName( element ) );
					if ( l == null ) {
						l = new Integer( 0 );
					}
					l = new Integer( l.intValue() + 1 );
					items.put( tmpSession.getEntityName( element ), l );
					System.out.println( "Data left: " + element );
				}
				fail( "Data is left in the database: " + items.toString() );
			}
		}
		finally {
			try {
				tmpSession.close();
			}
			catch( Throwable t ) {
				// intentionally empty
			}
		}
	
protected voidcleanupTest()

	
public voidconfigure(org.hibernate.cfg.Configuration cfg)

	
public booleancreateSchema()

		return true;
	
protected booleandialectIs(java.lang.Class dialectClass)

		return dialectClass.isInstance( getDialect() );
	
protected booleandialectIsCaseSensitive(java.lang.String testDescription)
Is the db/dialect sensitive in terms of string comparisons?

param
testDescription description of the scenario being tested.
return
true if sensitive

		if ( ! getDialect().areStringComparisonsCaseInsensitive() ) {
			reportSkip( "Dialect is case sensitive. ", testDescription );
			return true;
		}
		return false;
	
protected booleandialectIsNot(java.lang.Class dialectClass)

		return ! dialectIs( dialectClass );
	
protected booleandialectIsNot(java.lang.Class[] dialectClasses)

		return ! dialectIsOneOf( dialectClasses );
	
protected booleandialectIsOneOf(java.lang.Class[] dialectClasses)

		for ( int i = 0; i < dialectClasses.length; i++ ) {
			if ( dialectClasses[i].isInstance( getDialect() ) ) {
				return true;
			}
		}
		return false;
	
protected booleandialectSupportsEmptyInList(java.lang.String testDescription)
Does the db/dialect support empty lists in the IN operator?

For example, is "... a.b IN () ..." supported?

param
testDescription description of the scenario being tested.
return
true if is allowed

		if ( ! getDialect().supportsEmptyInList() ) {
			reportSkip( "Dialect does not support SQL empty in list : x in ()", testDescription );
			return false;
		}
		return true;
	
public java.lang.StringgetBaseForMappings()

		return "org/hibernate/test/";
	
public java.lang.StringgetCacheConcurrencyStrategy()

		return "nonstrict-read-write";
	
protected org.hibernate.cfg.ConfigurationgetCfg()

		return environment.getConfiguration();
	
protected org.hibernate.dialect.DialectgetDialect()

		return ExecutionEnvironment.DIALECT;
	
public ExecutionEnvironmentgetEnvironment()

		return environment;
	
protected org.hibernate.SessionFactorygetSessions()
Get the factory for this test environment.

return
The factory.

		return environment.getSessionFactory();
	
public org.hibernate.classic.SessionopenSession()

		session = getSessions().openSession();
		return session;
	
public org.hibernate.classic.SessionopenSession(org.hibernate.Interceptor interceptor)

		session = getSessions().openSession(interceptor);
		return session;
	
public booleanoverrideCacheStrategy()

		return true;
	
protected voidprepareTest()

	
protected booleanreadCommittedIsolationMaintained(java.lang.String scenario)
Is connection at least read committed?

Not, that this skip check relies on the JDBC driver reporting the true isolation level correctly. HSQLDB, for example, will report whatever you specify as the isolation (Connection.setTransationIsolation()), even though it only supports read-uncommitted.

param
scenario text description of the scenario being tested.
return
true if read-committed isolation is maintained.

		int isolation = java.sql.Connection.TRANSACTION_READ_UNCOMMITTED;
		Session testSession = null;
		try {
			testSession = openSession();
			isolation = testSession.connection().getTransactionIsolation();
		}
		catch( Throwable ignore ) {
		}
		finally {
			if ( testSession != null ) {
				try {
					testSession.close();
				}
				catch( Throwable ignore ) {
				}
			}
		}
		if ( isolation < java.sql.Connection.TRANSACTION_READ_COMMITTED ) {
			reportSkip( "environment does not support at least read committed isolation", scenario );
			return false;
		}
		else {
			return true;
		}
	
public booleanrecreateSchemaAfterFailure()

		return true;
	
protected voidrunTest()
runTest is overridden in order to apply session closure assertions.

throws
Throwable

		final boolean stats = sfi().getStatistics().isStatisticsEnabled();
		try {
			if ( stats ) {
				sfi().getStatistics().clear();
			}

			super.runTest();

			if ( stats ) {
				sfi().getStatistics().logSummary();
			}

			if ( session != null && session.isOpen() ) {
				if ( session.isConnected() ) {
					session.connection().rollback();
				}
				session.close();
				session = null;
				fail( "unclosed session" );
			}
			else {
				session = null;
			}
			assertAllDataRemoved();
		}
		catch ( Throwable e ) {
			log.trace( "test run resulted in error; attempting to cleanup", e );
			try {
				if ( session != null && session.isOpen() ) {
					if ( session.isConnected() ) {
						session.connection().rollback();
					}
					session.close();
				}
			}
			catch ( Exception ignore ) {
			}
			try {
				if ( recreateSchemaAfterFailure() && environment != null ) {
					environment.rebuild();
				}
			}
			catch ( Exception ignore ) {
			}
			throw e;
		}
	
public voidsetEnvironment(ExecutionEnvironment environment)

		this.environment = environment;
	
protected final voidsetUp()
Override {@link junit.framework.TestCase#setUp()} to check if we need to build a locally managed execution environment.

throws
Exception

		if ( environment == null ) {
			log.info( "Building locally managed execution env" );
			isEnvironmentLocallyManaged = true;
			environment = new ExecutionEnvironment( this );
			environment.initialize();
		}
		prepareTest();
	
protected org.hibernate.engine.SessionFactoryImplementorsfi()
Get the factory for this test environment, casted to {@link org.hibernate.engine.SessionFactoryImplementor}.

Shorthand for ( {@link org.hibernate.engine.SessionFactoryImplementor} ) {@link #getSessions()}...

return
The factory

		return ( SessionFactoryImplementor ) getSessions();
	
protected voidskipExpectedFailure(java.lang.Throwable error)

		super.skipExpectedFailure( error );
		try {
			if ( recreateSchemaAfterFailure() && environment != null ) {
				environment.rebuild();
			}
		}
		catch ( Exception ignore ) {
		}
	
protected booleansupportsCircularCascadeDelete()

		if ( ! getDialect().supportsCircularCascadeDeleteConstraints() ) {
			reportSkip( "db/dialect does not support 'circular' cascade delete constraints", "cascade delete constraint support" );
			return false;
		}
		return true;
	
protected booleansupportsExpectedLobUsagePattern()
Expected LOB usage pattern is such that I can perform an insert via prepared statement with a parameter binding for a LOB value without crazy casting to JDBC driver implementation-specific classes...

Part of the trickiness here is the fact that this is largely driver dependent. For Oracle, which is notoriously bad with LOB support in their drivers actually does a pretty good job with LOB support as of the 10.2.x versions of their drivers...

return
True if expected usage pattern is support; false otherwise.

		if ( ! getDialect().supportsExpectedLobUsagePattern() ) {
			reportSkip( "database/driver does not support expected LOB usage pattern", "LOB support" );
			return false;
		}
		return true;
	
protected booleansupportsLobValueChangePropogation()
Does the current dialect support propogating changes to LOB values back to the database? Talking about mutating the underlying value as opposed to supplying a new LOB instance...

return
True if the changes are propogated back to the database; false otherwise.

		if ( ! getDialect().supportsLobValueChangePropogation() ) {
			reportSkip( "database/driver does not support propogating LOB value change back to database", "LOB support" );
			return false;
		}
		return true;
	
protected booleansupportsResultSetPositionQueryMethodsOnForwardOnlyCursor()

		if ( ! getDialect().supportsResultSetPositionQueryMethodsOnForwardOnlyCursor() ) {
			reportSkip( "Driver does not support 'position query' methods on forward-only cursors", "query support" );
			return false;
		}
		return true;
	
protected booleansupportsRowValueConstructorSyntaxInInList()

		if ( ! getDialect().supportsRowValueConstructorSyntaxInInList() ) {
			reportSkip( "Dialect does not support 'tuple' syntax as part of an IN value list", "query support" );
			return false;
		}
		return true;
	
protected booleansupportsSubqueryOnMutatingTable()

		if ( !getDialect().supportsSubqueryOnMutatingTable() ) {
			reportSkip( "database/driver does not support referencing mutating table in subquery", "bulk DML support" );
			return false;
		}
		return true;
	
protected booleansupportsSubselectOnLeftSideIn()

		if ( ! getDialect().supportsSubselectAsInPredicateLHS() ) {
			reportSkip( "Database does not support (<subselect>) in ( ... ) ", "query support" );
			return false;
		}
		return true;
	
protected booleansupportsUnboundedLobLocatorMaterialization()
Is it supported to materialize a LOB locator outside the transaction in which it was created?

Again, part of the trickiness here is the fact that this is largely driver dependent.

NOTE: all database I have tested which {@link #supportsExpectedLobUsagePattern()} also support the ability to materialize a LOB outside the owning transaction...

return
True if unbounded materialization is supported; false otherwise.

		if ( !getDialect().supportsUnboundedLobLocatorMaterialization() ) {
			reportSkip( "database/driver does not support materializing a LOB locator outside the 'owning' transaction", "LOB support" );
			return false;
		}
		return true;
	
protected final voidtearDown()
Override {@link junit.framework.TestCase#tearDown()} to tear down the execution environment if it is locally managed.

throws
Exception

		cleanupTest();
		if ( isEnvironmentLocallyManaged ) {
			log.info( "Destroying locally managed execution env" );
			environment.complete();
			environment = null;
		}