diff --git a/core/testsuite/pom.xml b/core/testsuite/pom.xml index ab3fca21d8..928887806a 100644 --- a/core/testsuite/pom.xml +++ b/core/testsuite/pom.xml @@ -35,6 +35,7 @@ 1.8 ${test.java.version} com.blazebit.persistence.core.testsuite + com.blazebit.persistence.testsuite @@ -81,7 +82,6 @@ test - org.mockito @@ -115,7 +115,6 @@ activation ${version.activation} - @@ -167,12 +166,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoH2,${jpa.excludedGroups} + CoreTestsuite jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 admin admin org.h2.Driver + com.blazebit.persistence.testsuite.base.jpa.category.NoH2,${jpa.excludedGroups} + ${testBasePackage} @@ -220,12 +221,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoMySQL,com.blazebit.persistence.testsuite.base.jpa.category.NoMySQLOld,${jpa.excludedGroups} + CoreTestsuite jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8 root com.mysql.jdbc.Driver + com.blazebit.persistence.testsuite.base.jpa.category.NoMySQL,com.blazebit.persistence.testsuite.base.jpa.category.NoMySQLOld,${jpa.excludedGroups} + ${testBasePackage} @@ -273,12 +276,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoMySQL,${jpa.excludedGroups} + CoreTestsuite jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8 root com.mysql.jdbc.Driver + com.blazebit.persistence.testsuite.base.jpa.category.NoMySQL,${jpa.excludedGroups} + ${testBasePackage} @@ -326,13 +331,15 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoPostgreSQL,${jpa.excludedGroups} + CoreTestsuite jdbc:postgresql://localhost:5432/test postgres postgres org.postgresql.Driver public + com.blazebit.persistence.testsuite.base.jpa.category.NoPostgreSQL,${jpa.excludedGroups} + ${testBasePackage} @@ -380,12 +387,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoSQLite,${jpa.excludedGroups} + CoreTestsuite jdbc:sqlite:test.db org.sqlite.JDBC + com.blazebit.persistence.testsuite.base.jpa.category.NoSQLite,${jpa.excludedGroups} + ${testBasePackage} @@ -438,12 +447,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoDB2,${jpa.excludedGroups} + CoreTestsuite jdbc:db2://localhost:50000/test db2inst1 db2inst1-pwd com.ibm.db2.jcc.DB2Driver + com.blazebit.persistence.testsuite.base.jpa.category.NoDB2,${jpa.excludedGroups} + ${testBasePackage} @@ -498,12 +509,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoFirebird,${jpa.excludedGroups} + CoreTestsuite jdbc:firebirdsql:localhost:/tmp/test.fdb SYSDBA masterkey org.firebirdsql.jdbc.FBDriver + com.blazebit.persistence.testsuite.base.jpa.category.NoFirebird,${jpa.excludedGroups} + ${testBasePackage} @@ -551,7 +564,7 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoOracle,${jpa.excludedGroups} + CoreTestsuite jdbc:oracle:thin:@localhost:1521:XE SYSTEM @@ -563,6 +576,8 @@ true + com.blazebit.persistence.testsuite.base.jpa.category.NoOracle,${jpa.excludedGroups} + ${testBasePackage} @@ -610,12 +625,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoMSSQL,${jpa.excludedGroups} + CoreTestsuite jdbc:sqlserver://localhost:1433 sa Blaze-Persistence com.microsoft.sqlserver.jdbc.SQLServerDriver + com.blazebit.persistence.testsuite.base.jpa.category.NoMSSQL,${jpa.excludedGroups} + ${testBasePackage} diff --git a/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/CoreTestsuite.java b/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/CoreTestsuite.java new file mode 100644 index 0000000000..7e46534239 --- /dev/null +++ b/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/CoreTestsuite.java @@ -0,0 +1,30 @@ +/* + * Copyright 2014 - 2020 Blazebit. + * + * Licensed 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 com.blazebit.persistence.testsuite; + +import com.blazebit.persistence.testsuite.base.jpa.BlazePersistenceTestsuite; +import org.junit.runner.RunWith; +import org.junit.runners.AllTests; + +/** + * + * @author Moritz Becker + * @since 1.5.0 + */ +@RunWith(AllTests.class) +public class CoreTestsuite extends BlazePersistenceTestsuite { +} diff --git a/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/DateExtractTest.java b/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/DateExtractTest.java index 01602992b5..0e0bdc1d9b 100644 --- a/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/DateExtractTest.java +++ b/core/testsuite/src/test/java/com/blazebit/persistence/testsuite/DateExtractTest.java @@ -94,7 +94,7 @@ public static Collection producerConsumerTimezones() { protected boolean recreateDataSource() { // Some drivers have timezone information bound to the connection // So we have to recreate the data source to get the newly configured time zones for connections - boolean recreate = previousProducerTimeZone != null && previousClientTimeZone != null && (!producerTimeZone.equals(previousProducerTimeZone) || !clientTimeZone.equals(previousClientTimeZone)); + boolean recreate = !producerTimeZone.equals(previousProducerTimeZone) || !clientTimeZone.equals(previousClientTimeZone); previousProducerTimeZone = producerTimeZone; previousClientTimeZone = clientTimeZone; return recreate; diff --git a/entity-view/testsuite/pom.xml b/entity-view/testsuite/pom.xml index 19b4380af8..ff3d7217f5 100644 --- a/entity-view/testsuite/pom.xml +++ b/entity-view/testsuite/pom.xml @@ -35,6 +35,7 @@ 1.8 ${test.java.version} com.blazebit.persistence.view.testsuite + com.blazebit.persistence.view.testsuite @@ -170,12 +171,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoH2,${jpa.excludedGroups} + EntityViewTestsuite jdbc:h2:mem:test;DB_CLOSE_DELAY=-1 admin admin org.h2.Driver + com.blazebit.persistence.testsuite.base.jpa.category.NoH2,${jpa.excludedGroups} + ${testBasePackage} @@ -205,12 +208,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoMySQL,com.blazebit.persistence.testsuite.base.jpa.category.NoMySQLOld,${jpa.excludedGroups} + EntityViewTestsuite jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8 root com.mysql.jdbc.Driver + com.blazebit.persistence.testsuite.base.jpa.category.NoMySQL,com.blazebit.persistence.testsuite.base.jpa.category.NoMySQLOld,${jpa.excludedGroups} + ${testBasePackage} @@ -239,12 +244,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoMySQL,${jpa.excludedGroups} + EntityViewTestsuite jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8 root com.mysql.jdbc.Driver + com.blazebit.persistence.testsuite.base.jpa.category.NoMySQL,${jpa.excludedGroups} + ${testBasePackage} @@ -273,13 +280,15 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoPostgreSQL,${jpa.excludedGroups} + EntityViewTestsuite jdbc:postgresql://localhost:5432/test postgres postgres org.postgresql.Driver public + com.blazebit.persistence.testsuite.base.jpa.category.NoPostgreSQL,${jpa.excludedGroups} + ${testBasePackage} @@ -308,12 +317,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoSQLite,${jpa.excludedGroups} + EntityViewTestsuite jdbc:sqlite:test.db org.sqlite.JDBC + com.blazebit.persistence.testsuite.base.jpa.category.NoSQLite,${jpa.excludedGroups} + ${testBasePackage} @@ -347,12 +358,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoDB2,${jpa.excludedGroups} + EntityViewTestsuite jdbc:db2://localhost:50000/test:fullyMaterializeLobData=true;progressiveStreaming=2; db2inst1 db2inst1-pwd com.ibm.db2.jcc.DB2Driver + com.blazebit.persistence.testsuite.base.jpa.category.NoDB2,${jpa.excludedGroups} + ${testBasePackage} @@ -388,12 +401,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoFirebird,${jpa.excludedGroups} + EntityViewTestsuite jdbc:firebirdsql:localhost:/tmp/test.fdb SYSDBA masterkey org.firebirdsql.jdbc.FBDriver + com.blazebit.persistence.testsuite.base.jpa.category.NoFirebird,${jpa.excludedGroups} + ${testBasePackage} @@ -422,7 +437,7 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoOracle,${jpa.excludedGroups} + EntityViewTestsuite jdbc:oracle:thin:@localhost:1521:XE SYSTEM @@ -434,6 +449,8 @@ true + com.blazebit.persistence.testsuite.base.jpa.category.NoOracle,${jpa.excludedGroups} + ${testBasePackage} @@ -462,12 +479,14 @@ org.apache.maven.plugins maven-surefire-plugin - com.blazebit.persistence.testsuite.base.jpa.category.NoMSSQL,${jpa.excludedGroups} + EntityViewTestsuite jdbc:sqlserver://localhost:1433 sa Blaze-Persistence com.microsoft.sqlserver.jdbc.SQLServerDriver + com.blazebit.persistence.testsuite.base.jpa.category.NoMSSQL,${jpa.excludedGroups} + ${testBasePackage} diff --git a/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/EntityViewTestsuite.java b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/EntityViewTestsuite.java new file mode 100644 index 0000000000..70a5f837ab --- /dev/null +++ b/entity-view/testsuite/src/test/java/com/blazebit/persistence/view/testsuite/EntityViewTestsuite.java @@ -0,0 +1,30 @@ +/* + * Copyright 2014 - 2020 Blazebit. + * + * Licensed 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 com.blazebit.persistence.view.testsuite; + +import com.blazebit.persistence.testsuite.base.jpa.BlazePersistenceTestsuite; +import org.junit.runner.RunWith; +import org.junit.runners.AllTests; + +/** + * + * @author Moritz Becker + * @since 1.5.0 + */ +@RunWith(AllTests.class) +public class EntityViewTestsuite extends BlazePersistenceTestsuite { +} diff --git a/integration/spring-data/testsuite/webmvc/src/test/java/com/blazebit/persistence/spring/data/testsuite/webmvc/AbstractSpringTest.java b/integration/spring-data/testsuite/webmvc/src/test/java/com/blazebit/persistence/spring/data/testsuite/webmvc/AbstractSpringTest.java index 94a026b185..f0b5d11c4b 100644 --- a/integration/spring-data/testsuite/webmvc/src/test/java/com/blazebit/persistence/spring/data/testsuite/webmvc/AbstractSpringTest.java +++ b/integration/spring-data/testsuite/webmvc/src/test/java/com/blazebit/persistence/spring/data/testsuite/webmvc/AbstractSpringTest.java @@ -58,8 +58,8 @@ public void setUpContext() throws Exception { cleanDatabase(); this.em.getTransaction().rollback(); this.em.close(); - this.emf.close(); - this.emf = null; + emf.close(); + emf = null; this.em = null; this.cbf = null; this.jpaProvider = null; diff --git a/parent/pom.xml b/parent/pom.xml index 1778daa64e..a3490f3c4d 100644 --- a/parent/pom.xml +++ b/parent/pom.xml @@ -109,6 +109,7 @@ 2.4.5.Final 1.7.0.Final 2.0.4.Final + 4.8.89 3.3.2 @@ -579,6 +580,11 @@ ${mssql.version} test + + io.github.classgraph + classgraph + ${version.classgraph} + diff --git a/testsuite-base/datanucleus/src/main/java/com/blazebit/persistence/testsuite/base/AbstractPersistenceTest.java b/testsuite-base/datanucleus/src/main/java/com/blazebit/persistence/testsuite/base/AbstractPersistenceTest.java index a802618321..835555c470 100644 --- a/testsuite-base/datanucleus/src/main/java/com/blazebit/persistence/testsuite/base/AbstractPersistenceTest.java +++ b/testsuite-base/datanucleus/src/main/java/com/blazebit/persistence/testsuite/base/AbstractPersistenceTest.java @@ -22,6 +22,7 @@ import org.datanucleus.store.StoreManager; import org.datanucleus.store.connection.ConnectionManager; import org.datanucleus.store.connection.ManagedConnection; +import org.junit.Before; import javax.persistence.EntityManager; import java.lang.reflect.Method; @@ -35,6 +36,17 @@ */ public abstract class AbstractPersistenceTest extends AbstractJpaPersistenceTest { + // We need to recreate the emf because Datanucleus uses messed up metadata otherwise + @Before + public void recreateEmf() { + if (emf != null && emf.isOpen()) { + emf.close(); + } + emf = null; + em = null; + init(); + } + @Override protected Properties applyProperties(Properties properties) { properties.put("datanucleus.rdbms.mysql.characterSet", "utf8mb3"); diff --git a/testsuite-base/eclipselink/src/main/java/com/blazebit/persistence/testsuite/base/AbstractPersistenceTest.java b/testsuite-base/eclipselink/src/main/java/com/blazebit/persistence/testsuite/base/AbstractPersistenceTest.java index 97f5bd1297..499739308a 100644 --- a/testsuite-base/eclipselink/src/main/java/com/blazebit/persistence/testsuite/base/AbstractPersistenceTest.java +++ b/testsuite-base/eclipselink/src/main/java/com/blazebit/persistence/testsuite/base/AbstractPersistenceTest.java @@ -23,6 +23,7 @@ import org.eclipse.persistence.sessions.factories.SessionManager; import javax.persistence.EntityManager; +import javax.persistence.EntityManagerFactory; import javax.persistence.EntityTransaction; import java.sql.Connection; import java.util.HashMap; @@ -93,7 +94,8 @@ protected void dropSchema() { } } - protected void recreateOrClearSchema() { + @Override + protected EntityManagerFactory recreateOrClearSchema() { Map emSetupImpls = EntityManagerFactoryProvider.getEmSetupImpls(); Map copy = new HashMap<>(emSetupImpls); emSetupImpls.clear(); @@ -102,7 +104,7 @@ protected void recreateOrClearSchema() { manager.getSessions().remove(emSetupImpl.getSessionName()); } try { - super.recreateOrClearSchema(); + return super.recreateOrClearSchema(); } finally { emSetupImpls.putAll(copy); for (EntityManagerSetupImpl emSetupImpl : copy.values()) { diff --git a/testsuite-base/hibernate/src/main/java/com/blazebit/persistence/testsuite/base/AbstractPersistenceTest.java b/testsuite-base/hibernate/src/main/java/com/blazebit/persistence/testsuite/base/AbstractPersistenceTest.java index fe6eaf89d1..4681857094 100644 --- a/testsuite-base/hibernate/src/main/java/com/blazebit/persistence/testsuite/base/AbstractPersistenceTest.java +++ b/testsuite-base/hibernate/src/main/java/com/blazebit/persistence/testsuite/base/AbstractPersistenceTest.java @@ -174,6 +174,11 @@ protected Properties applyProperties(Properties properties) { // Needed for Envers tests in Hibernate >= 5.3.5, 5.4.x (HHH-12871) properties.put("hibernate.ejb.metamodel.population", "enabled"); + + if (isHibernate53Or54()) { + properties.put("hibernate.archive.scanner", "org.hibernate.boot.archive.scan.internal.DisabledScanner"); + } + // We use the following only for debugging purposes // Normally these settings should be disabled since the output would be too big TravisCI // properties.put("hibernate.show_sql", "true"); @@ -318,4 +323,12 @@ private boolean isHibernate526OrOlder() { int fix = Integer.parseInt(versionParts[2]); return major < 5 || major == 5 && minor < 2 || major == 5 && minor == 2 && fix < 7; } + + private boolean isHibernate53Or54() { + String version = org.hibernate.Version.getVersionString(); + String[] versionParts = version.split("[\\.-]"); + int major = Integer.parseInt(versionParts[0]); + int minor = Integer.parseInt(versionParts[1]); + return major == 5 && (minor == 3 || minor == 4); + } } diff --git a/testsuite-base/jpa/pom.xml b/testsuite-base/jpa/pom.xml index c24d0ac083..416decd6b7 100644 --- a/testsuite-base/jpa/pom.xml +++ b/testsuite-base/jpa/pom.xml @@ -32,6 +32,8 @@ com.blazebit.persistence.testsuite.base.jpa + 1.8 + 1.8 @@ -71,5 +73,9 @@ junit compile + + io.github.classgraph + classgraph + \ No newline at end of file diff --git a/testsuite-base/jpa/src/main/java/com/blazebit/persistence/testsuite/base/jpa/AbstractJpaPersistenceTest.java b/testsuite-base/jpa/src/main/java/com/blazebit/persistence/testsuite/base/jpa/AbstractJpaPersistenceTest.java index 161af99fe1..e31214e621 100644 --- a/testsuite-base/jpa/src/main/java/com/blazebit/persistence/testsuite/base/jpa/AbstractJpaPersistenceTest.java +++ b/testsuite-base/jpa/src/main/java/com/blazebit/persistence/testsuite/base/jpa/AbstractJpaPersistenceTest.java @@ -37,6 +37,7 @@ import net.ttddyy.dsproxy.listener.QueryExecutionListener; import net.ttddyy.dsproxy.support.ProxyDataSourceBuilder; import org.junit.After; +import org.junit.AfterClass; import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; @@ -82,6 +83,7 @@ */ public abstract class AbstractJpaPersistenceTest { + protected static EntityManagerFactory emf; private static boolean resolvedNoop = false; private static boolean databaseClean = false; private static Class lastTestClass; @@ -99,7 +101,6 @@ public abstract class AbstractJpaPersistenceTest { new OracleDatabaseCleaner.Factory() ); - protected EntityManagerFactory emf; protected EntityManager em; protected CriteriaBuilderFactory cbf; protected JpaProvider jpaProvider; @@ -143,33 +144,36 @@ protected final void cleanDatabaseWithCleaner() { return; } - boolean wasAutoCommit = false; - Connection connection = getConnection(getEm()); - try { - // Turn off auto commit if necessary - wasAutoCommit = connection.getAutoCommit(); - if (wasAutoCommit) { - connection.setAutoCommit(false); - } - // Clear the data with the cleaner - databaseCleaner.clearData(connection); - databaseClean = true; - } catch (SQLException ex) { + try (Connection connection = dataSource.getConnection()) { + boolean wasAutoCommit = false; try { - connection.rollback(); - } catch (SQLException e1) { - ex.addSuppressed(e1); - } - - throw new RuntimeException(ex); - } finally { - if (wasAutoCommit) { + // Turn off auto commit if necessary + wasAutoCommit = connection.getAutoCommit(); + if (wasAutoCommit) { + connection.setAutoCommit(false); + } + // Clear the data with the cleaner + databaseCleaner.clearData(connection); + databaseClean = true; + } catch (Exception ex) { try { - connection.setAutoCommit(true); - } catch (SQLException ex) { - throw new RuntimeException(ex); + connection.rollback(); + } catch (SQLException e1) { + ex.addSuppressed(e1); + } + + throw new RuntimeException(ex); + } finally { + if (wasAutoCommit) { + try { + connection.setAutoCommit(true); + } catch (SQLException ex) { + throw new RuntimeException(ex); + } } } + } catch (SQLException e) { + throw new RuntimeException(e); } } @@ -197,36 +201,16 @@ protected void clearCollections(EntityManager em, Class... entityClasses) { } private void clearSchema() { - clearSchema(getEm(), databaseCleaner); + clearSchema(databaseCleaner); } - public void clearSchema(EntityManager em, DatabaseCleaner databaseCleaner) { - boolean wasAutoCommit = false; - Connection connection = getConnection(em); - try { - // Turn off auto commit if necessary - wasAutoCommit = connection.getAutoCommit(); - if (wasAutoCommit) { - connection.setAutoCommit(false); - } + public void clearSchema(DatabaseCleaner databaseCleaner) { + try (Connection connection = dataSource.getConnection()) { + connection.setAutoCommit(false); // Clear the data with the cleaner databaseCleaner.clearSchema(connection); - } catch (SQLException ex) { - try { - connection.rollback(); - } catch (SQLException e1) { - ex.addSuppressed(e1); - } - - throw new RuntimeException(ex); - } finally { - if (wasAutoCommit) { - try { - connection.setAutoCommit(true); - } catch (SQLException ex) { - throw new RuntimeException(ex); - } - } + } catch (SQLException e) { + throw new RuntimeException(e); } } @@ -293,21 +277,24 @@ public void init() { recreateTestClass = getClass(); dataSource.close(); dataSource = null; + closeEmf(); } else if (recreateTestClass != null && recreateTestClass != getClass()) { recreateTestClass = null; if (dataSource != null) { dataSource.close(); dataSource = null; } + closeEmf(); } - emf = createEntityManagerFactory("TestsuiteBase", createProperties("none")); - // Disable query collecting QueryInspectorListener.enabled = false; QueryInspectorListener.collectSequences = false; if (!resolvedNoop && databaseCleaner == null) { + if (dataSource == null) { + getDataSource(createProperties("none")); + } try (Connection c = dataSource.getConnection()) { DatabaseCleaner applicableCleaner = getDatabaseCleaner(c); @@ -326,23 +313,30 @@ public void init() { setLastDatabaseCleaner(getDefaultDatabaseCleaner()); } + if (veryFirstTest || schemaChanged) { + getDataSource(createProperties("none")); + emf = recreateOrClearSchema(); + if (emf == null) { + emf = createEntityManagerFactory("TestsuiteBase", createProperties("none")); + } + } else if (emf == null || !emf.isOpen()) { + emf = createEntityManagerFactory("TestsuiteBase", createProperties("none")); + } + CriteriaBuilderConfiguration config = Criteria.getDefault(); config = configure(config); cbf = config.createCriteriaBuilderFactory(emf); jpaProvider = cbf.getService(JpaProvider.class); dbmsDialect = cbf.getService(DbmsDialect.class); - if (veryFirstTest || schemaChanged || !databaseCleaner.supportsClearSchema()) { - recreateOrClearSchema(); - setUpOnce(); - } else if (firstTest) { + getEm(); + if (firstTest) { setUpOnce(); } if (runTestInTransaction() && !getEm().getTransaction().isActive()) { getEm().getTransaction().begin(); } - getEm(); } private EntityManager getEm() { @@ -387,15 +381,12 @@ protected static void resetTimeZoneCaches() { } } - protected void createSchema() { + protected EntityManagerFactory createSchema() { EntityManagerFactory entityManagerFactory = createEntityManagerFactory("TestsuiteBase", createProperties("create")); if (needsEntityManagerForDbAction()) { - try { - entityManagerFactory.createEntityManager().close(); - } finally { - entityManagerFactory.close(); - } + entityManagerFactory.createEntityManager().close(); } + return entityManagerFactory; } protected void dropSchema() { @@ -409,23 +400,20 @@ protected void dropSchema() { } } - protected void dropAndCreateSchema() { + protected EntityManagerFactory dropAndCreateSchema() { EntityManagerFactory entityManagerFactory = createEntityManagerFactory("TestsuiteBase", createProperties("drop-and-create")); if (needsEntityManagerForDbAction()) { - try { - entityManagerFactory.createEntityManager().close(); - } finally { - entityManagerFactory.close(); - } + entityManagerFactory.createEntityManager().close(); } + return entityManagerFactory; } - protected void recreateOrClearSchema() { + protected EntityManagerFactory recreateOrClearSchema() { if (databaseCleaner.supportsClearSchema()) { clearSchema(); - createSchema(); + return createSchema(); } else { - dropAndCreateSchema(); + return dropAndCreateSchema(); } } @@ -451,21 +439,14 @@ protected boolean doesJpaMergeOfRecentlyPersistedEntityForceUpdate() { @After public void destruct() { - EntityManagerFactory factory; // NOTE: We need to close the entity manager or else we could run into a deadlock on some dbms platforms // I am looking at you MySQL.. if (em != null && em.isOpen()) { - factory = em.getEntityManagerFactory(); if (em.getTransaction().isActive()) { em.getTransaction().rollback(); } em.close(); em = null; - } else { - factory = emf; - } - if (factory != null && factory.isOpen()) { - factory.close(); } if (databaseCleaner != null && !databaseCleaner.supportsClearSchema()) { @@ -475,6 +456,15 @@ public void destruct() { if (dataSource != null && recreateDataSource()) { dataSource.close(); dataSource = null; + closeEmf(); + } + } + + @AfterClass + public static void closeEmf() { + if (emf != null && emf.isOpen()) { + emf.close(); + emf = null; } } diff --git a/testsuite-base/jpa/src/main/java/com/blazebit/persistence/testsuite/base/jpa/BlazePersistenceTestsuite.java b/testsuite-base/jpa/src/main/java/com/blazebit/persistence/testsuite/base/jpa/BlazePersistenceTestsuite.java new file mode 100644 index 0000000000..16d7596a8e --- /dev/null +++ b/testsuite-base/jpa/src/main/java/com/blazebit/persistence/testsuite/base/jpa/BlazePersistenceTestsuite.java @@ -0,0 +1,212 @@ +/* + * Copyright 2014 - 2020 Blazebit. + * + * Licensed 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 com.blazebit.persistence.testsuite.base.jpa; + +import io.github.classgraph.ClassGraph; +import io.github.classgraph.ClassInfo; +import io.github.classgraph.ClassInfoList; +import io.github.classgraph.MethodInfoList; +import io.github.classgraph.ScanResult; +import junit.framework.JUnit4TestAdapter; +import junit.framework.TestSuite; +import org.junit.Test; +import org.junit.experimental.categories.Categories; +import org.junit.runner.manipulation.NoTestsRemainException; +import org.junit.runners.Parameterized; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.*; +import java.util.stream.Collectors; + +/** + * We use dynamic testsuites to group test executions by the used entity classes in order to minimize schema dropping + * and entity manager factory recreation inbetween tests. + * + * @author Moritz Becker + * @since 1.5.0 + */ +public abstract class BlazePersistenceTestsuite { + + public static TestSuite suite() { + Class[] excludedGroups = loadExcludedGroups(); + TestClasses testClasses = loadAndGroupTestClasses(); + + TestSuite suite = new TestSuite(); + + testClasses.groupedJpaPersistenceTests.values().stream() + .sorted(Comparator.comparing(group -> + group.stream().map(Class::getCanonicalName).sorted().collect(Collectors.joining()) + )) + .flatMap(List::stream) + .map(testClass -> { + JUnit4TestAdapter jUnit4TestAdapter = new JUnit4TestAdapter(testClass); + try { + jUnit4TestAdapter.filter(Categories.CategoryFilter.exclude(excludedGroups)); + return jUnit4TestAdapter; + } catch (NoTestsRemainException e) { + return null; + } + }) + .filter(Objects::nonNull) + .forEach(suite::addTest); + + testClasses.nonJpaPersistenceTests.stream() + .sorted(Comparator.comparing(Class::getCanonicalName)) + .map(testClass -> { + JUnit4TestAdapter jUnit4TestAdapter = new JUnit4TestAdapter(testClass); + try { + jUnit4TestAdapter.filter(Categories.CategoryFilter.exclude(excludedGroups)); + return jUnit4TestAdapter; + } catch (NoTestsRemainException e) { + return null; + } + }) + .filter(Objects::nonNull) + .forEach(suite::addTest); + + return suite; + } + + private static TestClasses loadAndGroupTestClasses() { + String testsuitePackage = System.getProperty("testBasePackage"); + Map>, List>> groupedJpaPersistenceTests; + Set> nonJpaPersistenceTests = new HashSet<>(); + try (ScanResult scanResult = + new ClassGraph() + .enableAnnotationInfo() + .enableMethodInfo() + .enableClassInfo() + .enableExternalClasses() + .acceptPackages(testsuitePackage) + .scan()) { + + ClassInfoList allTests = scanResult.getClassesWithMethodAnnotation(Test.class.getName()); + ClassInfoList potentialJpaPersistenceTests = scanResult.getSubclasses(AbstractJpaPersistenceTest.class.getName()); + + Set jpaPersistenceTests = new HashSet<>(); + for (ClassInfo testClass : allTests) { + if (potentialJpaPersistenceTests.contains(testClass)) { + jpaPersistenceTests.add(testClass); + } else { + try { + nonJpaPersistenceTests.add(Thread.currentThread().getContextClassLoader().loadClass(testClass.getName())); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + } + + groupedJpaPersistenceTests = jpaPersistenceTests.stream() + .collect(Collectors.groupingBy( + jpaPersistenceTestInfo -> { + AbstractJpaPersistenceTest jpaPersistenceTest = instantiateAbstractJpaPersistenceTest(jpaPersistenceTestInfo); + return getEntityClasses(jpaPersistenceTest); + }, + Collectors.mapping(jpaPersistenceTestInfo -> { + try { + return (Class) Thread.currentThread().getContextClassLoader().loadClass(jpaPersistenceTestInfo.getName()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + }, Collectors.toList()) + )); + } + return new TestClasses(groupedJpaPersistenceTests, nonJpaPersistenceTests); + } + + private static Class[] loadExcludedGroups() { + String excludedGroupsProperty = System.getProperty("excludedGroups"); + Class[] excludedGroups; + if (excludedGroupsProperty == null) { + excludedGroups = new Class[0]; + } else { + excludedGroups = Arrays.stream(excludedGroupsProperty.split(",")) + .map(groupName -> { + try { + return Thread.currentThread().getContextClassLoader().loadClass(groupName.trim()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + }).toArray(Class[]::new); + } + return excludedGroups; + } + + private static Set> getEntityClasses(AbstractJpaPersistenceTest jpaPersistenceTest) { + try { + Method m = AbstractJpaPersistenceTest.class.getDeclaredMethod("getEntityClasses"); + m.setAccessible(true); + return new HashSet<>(Arrays.asList((Class[]) m.invoke(jpaPersistenceTest))); + } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } + + private static AbstractJpaPersistenceTest instantiateAbstractJpaPersistenceTest(ClassInfo testClassInfo) { + AbstractJpaPersistenceTest testInstance; + Class testClass; + try { + testClass = (Class) Thread.currentThread().getContextClassLoader().loadClass(testClassInfo.getName()); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + MethodInfoList parametersMethods = testClassInfo.getMethodInfo() + .filter(methodInfo -> methodInfo.getAnnotationInfo(Parameterized.Parameters.class.getName()) != null); + if (parametersMethods.isEmpty()) { + try { + testInstance = testClass.newInstance(); + } catch (InstantiationException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } else if (parametersMethods.size() == 1) { + try { + Method parametersMethod = testClass.getMethod(parametersMethods.get(0).getName()); + Object parameters = parametersMethod.invoke(null); + Object[] firstParameterSet; + if (parameters instanceof Collection) { + firstParameterSet = (Object[]) ((Collection) parameters).iterator().next(); + } else { + firstParameterSet = (Object[]) ((Object[]) parameters)[0]; + } + Constructor targetConstructor = (Constructor) Arrays.stream(testClass.getConstructors()) + .filter(constructor -> constructor.getParameterCount() == firstParameterSet.length) + .findFirst().get(); + testInstance = targetConstructor.newInstance(firstParameterSet); + } catch (NoSuchMethodException | IllegalAccessException | InstantiationException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } else { + throw new RuntimeException( + "Found more than 1 method annotated with " + + Parameterized.Parameters.class.getName() + + " in test class " + testClassInfo.getName()); + } + return testInstance; + } + + private static final class TestClasses { + private final Map>, List>> groupedJpaPersistenceTests; + private final Set> nonJpaPersistenceTests; + + private TestClasses(Map>, List>> groupedJpaPersistenceTests, Set> nonJpaPersistenceTests) { + this.groupedJpaPersistenceTests = groupedJpaPersistenceTests; + this.nonJpaPersistenceTests = nonJpaPersistenceTests; + } + } +}