/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.testing.framework; import java.io.*; import java.util.*; import javax.persistence.*; import org.eclipse.persistence.config.PersistenceUnitProperties; import org.eclipse.persistence.sessions.*; import org.eclipse.persistence.sessions.server.*; /** * <p> * <b>Purpose</b>: Allow for a test or set of tests to be executed and manage their execution and results. * <p> * <b>Responsibilities</b>: * <ul> * <li> Execute the test entity. * <li> Sets a flag to either log the results of test or not. * <li> Handles the exception returned by the test entity. * </ul> */ public class TestExecutor { /** It is a Stream where the test entity results are logged into */ protected Writer log; /** This attribute is added to migrate tests to Ora*Tst */ protected Writer regressionLog; /** Session used to run tests. */ protected Session session; /** EntityManagerFactory used to run JPA tests. */ protected EntityManagerFactory entityManagerFactory; /** Allows the original test session to be stored and repalced. */ protected Session originalSession; /** A boolean, when set to true would start handling the exceptions thrown by TopLink * and would not when set to false. */ protected boolean shouldHandleErrors; /** When set to true would log the results */ protected boolean shouldLogResults; protected Hashtable loadedModels; /** Contains a collection of all the configured systems */ protected Vector configuredSystems; /** This is used to stop execution thread */ protected boolean shouldStopExecution; /** Used for test event progress notification */ protected junit.framework.TestListener listener; /** Hold a default executor. Used to cache the executor for tests run in JUnit. */ protected static TestExecutor executor; /** Hold a default JUnit TestResult. Used to cache the result for JUnit tests run by the executor. */ protected static junit.framework.TestResult defaultJUnitTestResult; /** Hold JUnit TestResult but test. Used to store the results for JUnit tests run by the executor. */ protected static Map junitTestResults; /** This is used to get rid pf tests running on server(OC4J) */ public boolean isServer = false; /** Allow only errors to be logged. */ protected boolean shouldLogOnlyErrors = false; public static String CR = org.eclipse.persistence.internal.helper.Helper.cr(); /** * Return a default executor. Used as the executor for tests run in JUnit, or by themselves. */ public static TestExecutor getDefaultExecutor() { if (executor == null) { TestExecutor testExecutor = new TestExecutor(); testExecutor.setSession((new TestSystem()).login()); // Ensure connect successful before setting executor. executor = testExecutor; } return executor; } /** * Return if only errors should be logged. */ public boolean shouldLogOnlyErrors() { return shouldLogOnlyErrors; } /** * Set if only errors should be logged. */ public void setShouldLogOnlyErrors(boolean shouldLogOnlyErrors) { this.shouldLogOnlyErrors = shouldLogOnlyErrors; } /** * Set the default executor. Used as the executor for tests run in JUnit, or by themselves. */ public static void setDefaultExecutor(TestExecutor theExecutor) { executor = theExecutor; } /** * Set the default JUnit TestResult. Used to cache the result for JUnit tests run by the executor. */ public static void setDefaultJUnitTestResult(junit.framework.TestResult theTestResult) { defaultJUnitTestResult = theTestResult; } /** * Return the default JUnit TestResult. Used to cache the result for JUnit tests run by the executor. */ public static junit.framework.TestResult getDefaultJUnitTestResult() { return defaultJUnitTestResult; } /** * Set the JUnit TestResults. Used to store the result for JUnit tests run by the executor. */ public static void setJUnitTestResults(Map results) { junitTestResults = results; } /** * Return the JUnit TestResults. Used to store the result for JUnit tests run by the executor. */ public static Map getJUnitTestResults() { if (junitTestResults == null) { junitTestResults = new HashMap(); } return junitTestResults; } public TestExecutor() { this.log = new OutputStreamWriter(System.out); this.shouldLogResults = true; this.shouldHandleErrors = false; this.shouldStopExecution = false; this.configuredSystems = new Vector(); } /** * INTERNAL: * Add persistent system to the executor configuration. */ public void addConfigureSystem(TestSystem system) { if (!configuredSystemsContainsInstanceOf(system)) { getConfiguredSystems().addElement(system); } } /** * The loaded models hold all model in use to allow test case * The access other model to reuse their to setup. */ public void addLoadedModels(Vector models) { for (Enumeration theModels = models.elements(); theModels.hasMoreElements();) { TestModel model = (TestModel)theModels.nextElement(); getLoadedModels().put(model.getName(), model); } } /** * If the system is not already configured then configure it and store it in the executor. */ public void configureSystem(TestSystem system) throws Exception { if (!configuredSystemsContainsInstanceOf(system)) { system.run(getSession()); getSession().getIdentityMapAccessor().initializeAllIdentityMaps(); getConfiguredSystems().addElement(system); } } /** * Return true if the configuredSystems contains an instance of the class of the TestSystem parameter. */ public boolean configuredSystemsContainsInstanceOf(TestSystem system) { for (Enumeration configuredSystemsEnum = getConfiguredSystems().elements(); configuredSystemsEnum.hasMoreElements();) { if (configuredSystemsEnum.nextElement().getClass().equals(system.getClass())) { return true; } } return false; } /** * PUBLIC: * The executor stops handling errors and throws them to the user. This is mainly * for testing purpose to actully know the place of problem. */ public void doNotHandleErrors() { setShouldHandleErrors(false); } /** * PUBLIC: * The executor would stop logging error to the log stream. */ public void doNotLogResults() { setShouldLogResults(false); } public void doNotStopExecution() { setShouldStopExecution(false); } /** * PUBLIC: * To execute any test entity. */ public void execute(junit.framework.Test test) throws Throwable { if (shouldStopExecution()) { return; } try { getSession().logMessage("Begin " + test); if (getListener() != null) { getListener().startTest(test); } // If the suite was run through JUnit, or is a Junit suite, // run through the result, otherwise through the executor. if ((getDefaultJUnitTestResult() != null) || (!(test instanceof TestEntity))) { junit.framework.TestResult result = getDefaultJUnitTestResult(); if (getDefaultJUnitTestResult() == null) { result = new junit.framework.TestResult(); result.addListener(getListener()); getJUnitTestResults().put(test, result); } test.run(result); } else { ((TestEntity)test).execute(this); } if (getListener() != null) { getListener().endTest(test); } getSession().logMessage("Finished " + test); if (getAbstractSession().isInTransaction()) { throw new TestProblemException(test + " is a faulty test, transaction was left open and must always be closed."); } } catch (Throwable exception) { // Always catch warnings, and handle errors if shouldHandleErrors set. if ((!(exception instanceof TestWarningException)) && (!shouldHandleErrors())) { throw exception; } if (getListener() != null) { getListener().endTest(test); } getSession().logMessage("Failed " + test); } } /** * Forced system to be configured even if it is already configured. */ public void forceConfigureSystem(TestSystem system) throws Exception { removeConfigureSystem(system); system.run(getSession()); addConfigureSystem(system); } /** * Return all the configured systems. */ public Vector getConfiguredSystems() { return configuredSystems; } /** * Used for test event progress notifiaction. */ public junit.framework.TestListener getListener() { return listener; } /** * Return the model by name. * If missing null is returned. */ public TestModel getLoadedModel(String modelsName) { return (TestModel)getLoadedModels().get(modelsName); } /** * The loaded models hold all model in use to allow test case * The access other model to reuse their to setup. */ public Hashtable getLoadedModels() { return loadedModels; } /** * Return the log stream to print the results on. Default is console. * This method is added to migrate to Ora*Tst */ public Writer getRegressionLog() { return regressionLog; } /** * Return the log stream to print the results on. Default is console. */ public Writer getLog() { if (getSession() == null) { return log; } return getSession().getLog(); } /** * Create a new entity manager from the entity manager factory. * This entity manager is initialized from META-INF/persistence.xml. */ public EntityManager createEntityManager() { return getEntityManagerFactory().createEntityManager(); } /** * Return the executor entity manager factory. * This lazy initializes from the "performance" persistent unit using the default provider, * and configures the TopLink properties to connect to the executor's session's login. */ public EntityManagerFactory getEntityManagerFactory() { if (entityManagerFactory == null) { Map properties = getEntityManagerProperties(); entityManagerFactory = Persistence.createEntityManagerFactory("performance", properties); } return entityManagerFactory; } /** * Return the executor entity manager factory. * This lazy initializes from the "performance" persistent unit using the default provider, * and configures the TopLink properties to connect to the executor's session's login. */ public Map getEntityManagerProperties() { Map properties = new HashMap(); properties.put(PersistenceUnitProperties.JDBC_DRIVER, getSession().getLogin().getDriverClassName()); properties.put(PersistenceUnitProperties.JDBC_URL, getSession().getLogin().getConnectionString()); properties.put(PersistenceUnitProperties.JDBC_USER, getSession().getLogin().getUserName()); properties.put(PersistenceUnitProperties.JDBC_PASSWORD, getSession().getLogin().getPassword()); properties.put(PersistenceUnitProperties.LOGGING_LEVEL, getSession().getSessionLog().getLevelString()); return properties; } /** * Set the executor entity manager factory. */ public void setEntityManagerFactory(EntityManagerFactory entityManagerFactory) { this.entityManagerFactory = entityManagerFactory; } /** * Return the session cast to DatabaseSession. */ public org.eclipse.persistence.sessions.DatabaseSession getDatabaseSession() { return (org.eclipse.persistence.sessions.DatabaseSession)getSession(); } /** * Return the session cast to AbstractSession. */ public org.eclipse.persistence.internal.sessions.AbstractSession getAbstractSession() { return (org.eclipse.persistence.internal.sessions.AbstractSession)getSession(); } /** * Return the session. */ public Session getSession() { return session; } /** * Return the original session. */ public Session getOriginalSession() { return originalSession; } /** * Set the original session. */ public void setOriginalSession(Session originalSession) { this.originalSession = originalSession; } /** * Swap the current session with the new session. * This allows a test or model to customize the session it uses. */ public void swapSession(Session newSession) { setOriginalSession(getSession()); setSession(newSession); } /** * Swap the current session with a new database session with the same login, * but no descriptors. */ public void swapCleanDatabaseSession() { DatabaseSession session = new Project(getSession().getLogin()).createDatabaseSession(); session.setSessionLog(getSession().getSessionLog()); session.login(); swapSession(session); } /** * Swap the current session with the new server session. */ public void swapServerSession() { Server session = getSession().getProject().createServerSession(); session.setSessionLog(getSession().getSessionLog()); session.login(); swapSession(session); } /** * Swap the current session with the new session. * This allows a test or model to customize the session it uses. */ public void resetSession() { if(getOriginalSession() != null) { if(getDatabaseSession().isConnected()) { getDatabaseSession().logout(); } setSession(getOriginalSession()); setOriginalSession(null); } } /** * PUBLIC: * Starts handling errors. Even if some error is raised Executor just * catches it and goes on to execute the next test. */ public void handleErrors() { setShouldHandleErrors(true); } public void initializeConfiguredSystems() { setConfiguredSystems(new Vector()); } /** * This logs out from session. */ protected void logout() { if (session != null) { ((DatabaseSession)session).logout(); } } /** * Logs the result for the given test entity if logResults is true. * This method is added to migrate tests to Ora*Tst */ public void logRegressionResultForTestEntity(junit.framework.Test test) { logResultForTestEntity(test, true); } /** * Logs the result for the given test entity if logResults is true. */ public void logResultForTestEntity(junit.framework.Test test) { logResultForTestEntity(test, false); } /** * Logs the result for the given test entity if logResults is true. */ public void logResultForTestEntity(junit.framework.Test test, boolean regression) { Writer log = getLog(); if (regression) { log = getRegressionLog(); } if (shouldStopExecution()) { try { log.write("!!! THE TEST EXECUTION WAS INTERRUPTED !!!"); log.flush(); } catch (IOException e) { } } if (shouldLogResults()) { if (test instanceof TestEntity) { TestEntity testEntity = (TestEntity)test; testEntity.resetNestedCounter(); if (regression) { testEntity.logRegressionResult(log); } else { testEntity.logResult(log, shouldLogOnlyErrors()); } testEntity.resetNestedCounter(); } else { logJUnitResult(test, log, ""); } } try { log.flush(); } catch (IOException e) { } } /** * Log any JUnit results if present. */ public static void logJUnitResult(junit.framework.Test test, Writer log, String indent) { try { log.write(CR); log.write(CR); log.write(indent + "TEST MODEL NAME: (JUnit test): " + test); log.write(CR); junit.framework.TestResult result = (junit.framework.TestResult) TestExecutor.getJUnitTestResults().get(test); if (result == null) { log.write(indent + "## SETUP FAILURE ## (no tests run)"); log.write(CR); log.flush(); return; } if ((result.failureCount() > 0) || (result.errorCount() > 0)) { log.write(indent + "###ERRORS###" + CR); } log.write(CR); log.write(indent + "Errors: (failures): " + result.failureCount()); log.write(CR); log.write(indent + "Fatal Errors: (errors): " + result.errorCount()); log.write(CR); log.write(indent + "Passed: " + (result.runCount() - result.errorCount() - result.failureCount())); log.write(CR); log.write(indent + "Total Tests: " + result.runCount()); log.write(CR); if (result.failureCount() > 0) { log.write(CR); log.write(indent + "Failures:"); log.write(CR); for (Enumeration failures = result.failures(); failures.hasMoreElements();) { junit.framework.TestFailure failure = (junit.framework.TestFailure)failures.nextElement(); String testString = failure.failedTest().toString(); int startIndex = testString.indexOf("("); if (startIndex != -1) { log.write(indent + "TEST SUITE NAME: " + testString.substring(startIndex + 1, testString.length() - 1)); log.write(CR); } log.write(indent + "TEST NAME: " + testString); log.write(CR); log.write(indent + "##FAILURE##" + CR); log.write(indent + "RESULT: Error (failure)"); log.write(CR); log.write(indent + failure.trace()); log.write(CR); } } if (result.errorCount() > 0) { log.write(CR); log.write(indent + "Errors:"); log.write(CR); for (Enumeration errors = result.errors(); errors.hasMoreElements();) { junit.framework.TestFailure error = (junit.framework.TestFailure)errors.nextElement(); String testString = error.failedTest().toString(); int startIndex = testString.indexOf("("); if (startIndex != -1) { log.write(indent + "TEST SUITE NAME: " + testString.substring(startIndex + 1, testString.length() - 1)); log.write(CR); } log.write(indent + "TEST NAME: " + testString); log.write(CR); log.write(indent + "##FAILURE##" + CR); log.write(indent + "RESULT: FatalError (error)"); log.write(CR); log.write(indent + error.trace()); log.write(CR); } } log.write(CR); log.flush(); } catch (IOException exception) { } } /** * Log the results to the log stream. */ public void logResults() { setShouldLogResults(true); } /** * Public: * This method is used if we use dos promt to run our test cases. */ public static void main(String[] arguments) { try { TestExecutor executor = new TestExecutor(); // executor.handleErrors(); // executor.doNotLogResults(); executor.execute((TestEntity)Class.forName(arguments[0]).newInstance()); } catch (Throwable exception) { System.out.println(exception.toString()); } } public void removeConfigureSystem(TestSystem system) { removeFromConfiguredSystemsInstanceOf(system); } /** * If an instance of the same class as the parameter exists in the Vector of configuredSystems * then remove it. */ public void removeFromConfiguredSystemsInstanceOf(TestSystem system) { // find and record the systems to remove Vector systemsToRemove = new Vector(); for (Enumeration systemEnum = getConfiguredSystems().elements(); systemEnum.hasMoreElements();) { TestSystem aSystem = (TestSystem)systemEnum.nextElement(); if (aSystem.getClass().equals(system.getClass())) { systemsToRemove.addElement(aSystem); } } // Do the removing for (Enumeration systemsToRemoveEnum = systemsToRemove.elements(); systemsToRemoveEnum.hasMoreElements();) { getConfiguredSystems().removeElement(systemsToRemoveEnum.nextElement()); } } /** * The loaded models hold all model in use to allow test case * to access other model to reuse their to setup. */ public void resetLoadedModels() { setLoadedModels(new Hashtable()); } /** * PUBLIC: * This method executes the test entity. This method sets the session by using test * entity default login and once the execution is over it explicitily logs out. */ public void runTest(junit.framework.Test test) throws Throwable { boolean hasSession = true; setShouldStopExecution(false); if ((getSession() == null) && (test instanceof TestEntity)) { TestEntity testEntity = (TestEntity)test; hasSession = false; if (shouldHandleErrors()) { try { setSession(testEntity.defaultLogin()); } catch (Exception exception) { logout(); return; } } else { setSession(testEntity.defaultLogin()); } } try { execute(test); //This line is added to migrate tests to Ora*Tst if (getRegressionLog() != null) { logRegressionResultForTestEntity(test); } logResultForTestEntity(test); } finally { if (!hasSession) { logout(); } } } /** * Set configured systems. */ public void setConfiguredSystems(Vector configuredSystems) { this.configuredSystems = configuredSystems; } /** * Used for test event progress notification. */ public void setListener(junit.framework.TestListener listener) { this.listener = listener; } /** * The loaded models hold all model in use to allow test case * The access other model to reuse their to setup. */ protected void setLoadedModels(Hashtable loadedModels) { this.loadedModels = loadedModels; } /** * PUBLIC: * Set the log stream to which test results should be logged on. * This method is added to migrate to Ora*Tst */ public void setRegressionLog(Writer writer) { this.regressionLog = writer; } /** * PUBLIC: * Set the log stream to which test results should be logged on. */ public void setLog(Writer writer) { this.log = writer; } /** * PUBLIC: * Set the session */ public void setSession(Session theSession) { // Don't allow bad tests to set session to null. if (theSession == null) { return; } session = theSession; } public void setShouldHandleErrors(boolean aBoolean) { shouldHandleErrors = aBoolean; } public void setShouldLogResults(boolean aBoolean) { shouldLogResults = aBoolean; } public void setShouldStopExecution(boolean aBoolean) { shouldStopExecution = aBoolean; } /** * Returns if errors are to be handled or not. */ public boolean shouldHandleErrors() { return shouldHandleErrors; } /** * Returns if results should be logged or not. */ public boolean shouldLogResults() { return shouldLogResults; } /** * Returns if test entities should be execute or not. */ public boolean shouldStopExecution() { return shouldStopExecution; } public void stopExecution() { setShouldStopExecution(true); } }