| /* |
| * Copyright (c) 1998, 2021 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 v. 2.0 which is available at |
| * http://www.eclipse.org/legal/epl-2.0, |
| * or the Eclipse Distribution License v. 1.0 which is available at |
| * http://www.eclipse.org/org/documents/edl-v10.php. |
| * |
| * SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause |
| */ |
| |
| // Contributors: |
| // Oracle - initial API and implementation from Oracle TopLink |
| package org.eclipse.persistence.testing.framework; |
| |
| import java.io.*; |
| import java.util.*; |
| import jakarta.persistence.*; |
| |
| import junit.framework.TestFailure; |
| 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); |
| System.err.println(test + " FAILED"); |
| } |
| } |
| |
| /** |
| * 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<TestFailure> failures = result.failures(); failures.hasMoreElements();) { |
| junit.framework.TestFailure failure = 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<TestFailure> errors = result.errors(); errors.hasMoreElements();) { |
| junit.framework.TestFailure error = 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]).getConstructor().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); |
| } |
| } |