blob: 94826636f22a4e4507da9f9070d716ad359614b7 [file] [log] [blame]
/*
* 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 java.util.concurrent.ConcurrentHashMap;
import org.eclipse.persistence.internal.helper.ConversionManager;
import org.eclipse.persistence.sessions.DatabaseSession;
import org.eclipse.persistence.sessions.Project;
import org.eclipse.persistence.sessions.Session;
import org.eclipse.persistence.logging.SessionLog;
import org.eclipse.persistence.sessions.DatabaseLogin;
import org.eclipse.persistence.sessions.Login;
import org.eclipse.persistence.testing.framework.TestExecutor;
import org.eclipse.persistence.sessions.factories.SessionManager;
/**
* <p><b>Purpose</b>: Test model is a collection of test suites and/or sub test models. When a
* test model is executed all the test suites and models registered with it are
* executed one by one.
*/
public class TestModel extends TestCollection {
/** Configurations that must be set before this model is run */
private Vector requiredSystems;
/** The model will force these Configurations to be set even if they are set before */
private Vector forcedRequiredSystems;
/** Used to store tests added when the model is built. */
private Vector originalTests;
/** Keep track of setup state. */
private boolean isSetup;
/** Ensure the login and log are not corrupted if the session is changed. */
private Login login;
private SessionLog sessionLog;
/**
* Flag used to determine if tables should be recreated/populated before each test model.
* true means tests will take longer to run, but run more consistently as bad tests will not effect other models.
* Uses system property "org.eclipse.persistence.testing.reset-system".
*/
private static Boolean shouldResetSystemAfterEachTestModel;
/**
* Flag used to determine if tables should be recreated/populated before each test model.
* true means tests will take longer to run, but run more consistently as bad tests will not effect other models.
* Uses system property "org.eclipse.persistence.testing.reset-system".
*/
public static boolean shouldResetSystemAfterEachTestModel() {
if (shouldResetSystemAfterEachTestModel == null) {
String systemProperty = System.getProperty("org.eclipse.persistence.testing.reset-system");
if (systemProperty == null) {
shouldResetSystemAfterEachTestModel = Boolean.TRUE;
} else {
if (systemProperty.equals("false")) {
shouldResetSystemAfterEachTestModel = Boolean.FALSE;
} else {
shouldResetSystemAfterEachTestModel = Boolean.TRUE;
}
}
}
return shouldResetSystemAfterEachTestModel;
}
/**
* Flag used to determine if tables should be recreated/populated before each test model.
* true means tests will take longer to run, but run more consistently as bad tests will not effect other models.
* Uses system property "org.eclipse.persistence.testing.reset-system".
*/
public static void setShouldResetSystemAfterEachTestModel(boolean value) {
if (value) {
shouldResetSystemAfterEachTestModel = Boolean.TRUE;
} else {
shouldResetSystemAfterEachTestModel = Boolean.FALSE;
}
}
public TestModel() {
this.requiredSystems = new Vector();
this.forcedRequiredSystems = new Vector();
this.isSetup = false;
this.originalTests = new Vector();
}
/**
* The system add will be forced to be initialized again even if it is already configured.
* Basically this means that required system will recreate new database.
*/
public final void addForcedRequiredSystem(TestSystem requiredSystem) {
getForcedRequiredSystems().addElement(requiredSystem);
}
/**
* The system add will be forced to be initialized again even if it is already configured.
* Basically this means that required system will recreate new database. This method should always
* be overwritten in the subclasses if the model needs some persistence system to be already configured
* before it is run.
*/
public void addForcedRequiredSystems() {
}
/**
* The system add will be configured if it has not been already configured.
* Basically this means that required system will recreate new database.
*/
public final void addRequiredSystem(TestSystem requiredSystem) {
getRequiredSystems().addElement(requiredSystem);
}
/**
* The system add will be configured if it has not been already configured.
* Basically this means that required system will recreate new database.
* This method should always be overwritten in the subclasses if the model
* needs some persistence system to be already configured before it is run.
*/
public void addRequiredSystems() {
}
/**
* The subclasses must overwrite this method. To add tests to the model.
* It could be collection of test suites or test models themselves.
*/
@Override
public void addTests() {
}
/**
* The subclasses must overwrite this method. To add tests to the model.
* It could be collection of test suites or test models themselves.
*/
@Override
public void addSRGTests() {
}
/**
* Build the required systems, but ensure that the variable is not modified.
*/
public Vector buildForcedRequiredSystems() {
Vector constructedSystems = (Vector)getForcedRequiredSystems().clone();
addForcedRequiredSystems();
Vector allSystems = getForcedRequiredSystems();
setForcedRequiredSystems(constructedSystems);
return allSystems;
}
/**
* Build the required systems, but ensure that the variable is not modified.
*/
public Vector buildRequiredSystems() {
Vector constructedSystems = (Vector)getRequiredSystems().clone();
addRequiredSystems();
Vector allSystems = getRequiredSystems();
setRequiredSystems(constructedSystems);
return allSystems;
}
/**
* Goes through each systems and configures them.
*/
private void configure() throws Exception {
Vector systems = buildRequiredSystems();
for (Enumeration enumtr = systems.elements(); enumtr.hasMoreElements();) {
TestSystem system = (TestSystem)enumtr.nextElement();
// To improve test consistency always force systems to be reset.
if (shouldResetSystemAfterEachTestModel()) {
getExecutor().forceConfigureSystem(system);
} else {
getExecutor().configureSystem(system);
}
}
systems = buildForcedRequiredSystems();
for (Enumeration enumtr = systems.elements(); enumtr.hasMoreElements();) {
TestSystem system = (TestSystem)enumtr.nextElement();
getExecutor().forceConfigureSystem(system);
}
}
/**
* Executes all the test entities in the collection.
*/
@Override
public void execute(TestExecutor executor) throws Throwable {
setSummary(new TestResultsSummary(this));
setExecutor(executor);
long startTime = System.nanoTime();
try {
setupEntity();
setFinishedTests(new Vector());
try {
for (Enumeration tests = getTests().elements(); tests.hasMoreElements();) {
junit.framework.Test test = (junit.framework.Test)tests.nextElement();
if ((TestExecutor.getDefaultJUnitTestResult() != null) && TestExecutor.getDefaultJUnitTestResult().shouldStop()) {
break;
}
executor.execute(test);
getFinishedTests().addElement(test);
}
} catch (Throwable exception) {
try {
resetEntity();
} catch (Throwable ignore) {
}
throw exception;
}
resetEntity();
} finally {
long endTime = System.nanoTime();
getSummary().setTotalTime(endTime - startTime);
}
}
/**
* Return all the required systems that need to be configured even if they are already configured.
*/
public Vector getForcedRequiredSystems() {
return forcedRequiredSystems;
}
/**
* Return test that existed before setup.
*/
protected Vector getOriginalTests() {
return originalTests;
}
/**
* Return all the required systems that need to be configured if they are not already configured.
*/
public Vector getRequiredSystems() {
return requiredSystems;
}
public boolean isSetup() {
return isSetup;
}
/**
* Format the test output on the print stream.
*/
@Override
protected void logFootNote(Writer log) {
try {
log.write(org.eclipse.persistence.internal.helper.Helper.cr() + getIndentationString() + "RESULTS OF TEST MODEL: " + getName() + org.eclipse.persistence.internal.helper.Helper.cr());
} catch (IOException exception) {
}
}
/**
* Format the test output on the print stream.
* This method is added to migrate tests to Ora*Tst
*/
@Override
protected void logRegressionHeadNote(Writer log) {
try {
log.write(org.eclipse.persistence.internal.helper.Helper.cr() + getIndentationString() + "TEST MODEL NAME: " + getName() + org.eclipse.persistence.internal.helper.Helper.cr());
log.write(getIndentationString() + "MODEL DESCRIPTION: " + getDescription() + org.eclipse.persistence.internal.helper.Helper.cr());
} catch (IOException exception) {
}
}
/**
* Format the test output on the print stream.
*/
@Override
protected void logHeadNote(Writer log) {
try {
log.write(org.eclipse.persistence.internal.helper.Helper.cr() + getIndentationString() + "VERSION: " + org.eclipse.persistence.sessions.DatabaseLogin.getVersion());
log.write(org.eclipse.persistence.internal.helper.Helper.cr() + getIndentationString() + "TEST MODEL NAME: " + getName() + org.eclipse.persistence.internal.helper.Helper.cr());
log.write(getIndentationString() + "MODEL DESCRIPTION: " + getDescription() + org.eclipse.persistence.internal.helper.Helper.cr());
} catch (IOException exception) {
}
}
/**
* This is a optional method and it should be overridden if their is something
* that test collection should perform after running itself.
*/
public void reset() {
return;
}
/**
* If setupEntity has been called then this must be called to reset the model again.
*/
@Override
public void resetEntity() {
if (isSetup()) {
setTests(getOriginalTests());
setIsSetup(false);
}
if(this.getSummary().didSetupWarn()) {
return;
}
reset();
// To improve test consistency cleanup the session and executor better.
getSession().getIdentityMapAccessor().initializeIdentityMaps();
if (shouldResetSystemAfterEachTestModel()) {
getExecutor().setConfiguredSystems(new Vector());
// Logout and clean/reset the session in case test model failed ungracefully.
if (getSession().isDatabaseSession()) {
try {
getDatabaseSession().logout();
} catch (Exception ignore) {
}
}
if (this.login == null) {
this.login = getSession().getDatasourceLogin();
this.sessionLog = getSession().getSessionLog();
}
// Check if login or log were corrupted.
if (this.login.getClass() != getSession().getDatasourceLogin().getClass()) {
System.out.println("Login changed by test model:" + this);
}
if (this.sessionLog.getLevel() != getSession().getSessionLog().getLevel()) {
System.out.println("Log level changed by test model:" + this);
}
if (this.login instanceof DatabaseLogin) {
DatabaseLogin login = (DatabaseLogin)this.login;
if (login.shouldBindAllParameters() != getSession().getLogin().shouldBindAllParameters()) {
System.out.println("Binding changed by test model:" + this);
}
if (login.shouldCacheAllStatements() != getSession().getLogin().shouldCacheAllStatements()) {
System.out.println("Statement caching changed by test model:" + this);
}
if (login.shouldUseBatchWriting() != getSession().getLogin().shouldUseBatchWriting()) {
System.out.println("Batch writing changed by test model:" + this);
}
if (login.shouldUseJDBCBatchWriting() != getSession().getLogin().shouldUseJDBCBatchWriting()) {
System.out.println("JDBC batch writing changed by test model:" + this);
}
if (login.shouldUseNativeSQL() != getSession().getLogin().shouldUseNativeSQL()) {
System.out.println("Native SQL changed by test model:" + this);
}
if (login.getTableQualifier() != getSession().getLogin().getTableQualifier()) {
System.out.println("Table qualifier changed by test model:" + this);
}
}
DatabaseSession session = new Project(this.login).createDatabaseSession();
session.setSessionLog(this.sessionLog);
getExecutor().setSession(session);
// Check if default conversion manager was corrupted.
if (!ConversionManager.getDefaultManager().shouldUseClassLoaderFromCurrentThread()) {
System.out.println("ConversionManager corrupted by test model:" + this);
}
ConversionManager.setDefaultManager(null);
getSession().getDatasourceLogin().getDatasourcePlatform().setConversionManager(null);
SessionManager.getManager().setSessions(new ConcurrentHashMap<String, Session>());
getDatabaseSession().login();
}
setIsSetup(false);
}
/**
* Set all the required systems that need to be configured even if they are already configured.
*/
public void setForcedRequiredSystems(Vector systems) {
this.forcedRequiredSystems = systems;
}
protected void setIsSetup(boolean isSetup) {
this.isSetup = isSetup;
}
/**
* Set the test that existed before setup.
*/
protected void setOriginalTests(Vector originalTests) {
this.originalTests = originalTests;
}
/**
* Set all the required sytems that need to be configured if they are not already configured.
*/
public void setRequiredSystems(Vector systems) {
this.requiredSystems = systems;
}
/**
* This is a optional method and it should be overridden if their is something
* that test collection should perform before running itself.
*/
public void setup() {
return;
}
/**
* To set up the model also look at resetEntity
*/
@Override
public void setupEntity() throws Throwable {
if (isSetup()) {
return;
}
if (getSession() != null) {
// Force field names to uppercase for postgres.
if (getSession().getDatasourcePlatform().isPostgreSQL()) {
getSession().getPlatform().setShouldForceFieldNamesToUpperCase(true);
}
this.login = getSession().getDatasourceLogin().clone();
this.sessionLog = (SessionLog)getSession().getSessionLog().clone();
}
try {
setOriginalTests((Vector)getTests().clone());
configure();
setup();
if (isSRG) {
addSRGTests();
} else {
addTests();
}
// Check for faulty models leaving transaction open.
if ((getAbstractSession() != null) && getAbstractSession().isInTransaction()) {
try {
int count = 0;
while (getAbstractSession().isInTransaction() && (count < 10)) {
getAbstractSession().rollbackTransaction();
count++;
}
} catch (Throwable ignore) {
}
TestProblemException exception = new TestProblemException(this + " is a faulty test, transaction was left open and must always be closed.");
throw exception;
}
} catch (Throwable exception) {
getSummary().setSetupException(exception);
try {
resetEntity();
} catch (Throwable resetException) {
}
throw exception;
}
setIsSetup(true);
}
/**
* Returns the number of tests in this suite.
* If not setup, return the finished tests.
*/
@Override
public int testCount() {
if (isSetup() || (!getTests().isEmpty())) {
return super.testCount();
}
return getFinishedTests().size();
}
/**
* Returns the tests as an enumeration.
* If not setup, return the finished tests.
*/
@Override
public Enumeration tests() {
if (isSetup() || (!getTests().isEmpty())) {
return super.tests();
}
return getFinishedTests().elements();
}
/**
* Returns the test at the given index.
* If not setup, return the finished tests.
*/
@Override
public junit.framework.Test testAt(int index) {
if (isSetup() || (!getTests().isEmpty())) {
return super.testAt(index);
}
return (junit.framework.Test)getFinishedTests().elementAt(index);
}
}