/** * 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. * * Copyright 2012-2016 the original author or authors. */ package org.assertj.db.common; import com.ninja_squad.dbsetup.DbSetup; import com.ninja_squad.dbsetup.DbSetupTracker; import com.ninja_squad.dbsetup.destination.DriverManagerDestination; import com.ninja_squad.dbsetup.operation.Operation; import org.assertj.db.configuration.TestsConfiguration; import org.assertj.db.type.*; import org.assertj.db.type.lettercase.LetterCase; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.rules.TestName; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; import org.springframework.transaction.annotation.Transactional; import javax.sql.DataSource; import java.lang.annotation.Annotation; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.sql.*; import java.util.List; import java.util.logging.Logger; import static com.ninja_squad.dbsetup.Operations.*; /** * Parent for all the tests. It contains the variables like a {@code DataSource} and a {@code Source}. * * @author RĂ©gis Pouiller */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { TestsConfiguration.class }) @Transactional public abstract class AbstractTest { protected static final Logger LOG = Logger.getLogger("Test"); @Rule public TestName testNameRule = new TestName(); @Autowired(required = true) protected DataSource dataSource; protected final Source source = new Source("jdbc:h2:mem:test", "sa", ""); private static final DbSetupTracker dbSetupTracker = new DbSetupTracker(); private static final Operation DELETE_ALL = deleteAllFrom("test2", "test", "interpretation", "actor", "movie"); private static final Operation INSERT_MOVIE = sequenceOf( insertInto("movie").columns("id", "title", "year", "movie_imdb") .values(1, "Alien", 1979, "30B443AE-C0C9-4790-9BEC-CE1380808435") .values(2, "The Village", 2004, "16319617-AE95-4087-9264-D3D21BF611B6") .values(3, "Avatar", 2009, "D735221B-5DE5-4112-AA1E-49090CB75ADA").build()); private static final Operation INSERT_ACTOR = insertInto("actor") .columns("id", "name", "firstname", "birth", "actor_imdb") .values(1, "Weaver", "Sigourney", Date.valueOf("1949-10-08"), "30B443AE-C0C9-4790-9BEC-CE1380808435") .values(2, "Phoenix", "Joaquim", Date.valueOf("1974-10-28"), "16319617-AE95-4087-9264-D3D21BF611B6") .values(3, "Worthington", "Sam", Date.valueOf("1976-08-02"), "D735221B-5DE5-4112-AA1E-49090CB75ADA") .build(); private static final Operation INSERT_INTERPRETATION = insertInto("interpretation") .columns("id", "id_movie", "id_actor", "character") .values(1, 1, 1, "Ellen Louise Ripley") .values(2, 2, 1, "Alice Hunt") .values(3, 3, 1, "Dr Grace Augustine") .values(4, 2, 2, "Lucius Hunt") .values(5, 3, 3, "Jake Sully") .build(); private static final Operation INSERT_TEST = insertInto("test") .columns("var1", "var2", "var3", "var4", "var5", "var6", "var7", "var8", "var9", "var10", "var11", "var12", "var13", "var14", "var15") .values(1, true, 2, 3, 4, 5.6, 7.8, Time.valueOf("09:46:30"), Date.valueOf("2014-05-24"), Timestamp.valueOf("2014-05-24 09:46:30"), new byte[0], "text", 5, 7, "30B443AE-C0C9-4790-9BEC-CE1380808435") .values(10, false, 20, 30, 40, 50.6, 70.8, Time.valueOf("12:29:49"), Date.valueOf("2014-05-30"), Timestamp.valueOf("2014-05-30 12:29:49"), new byte[0], "another text", 50, 70, "0E2A1269-EFF0-4233-B87B-B53E8B6F164D") .values(100, false, 25, 300, 400, 500.6, 700.8, Time.valueOf("12:29:49"), Date.valueOf("2014-05-30"), Timestamp.valueOf("2014-05-30 00:00:00"), new byte[0], "another text again", 500, 700, "2B0D1BDD-909E-4362-BA10-C930BA82718D") .values(1000, false, 0, 0, 0, 0, 0, Time.valueOf("12:29:49"), Date.valueOf("2014-05-30"), Timestamp.valueOf("2014-05-30 00:00:00"), new byte[0], "another text again", 500, 700, "399FFFCA-7874-4225-9903-E227C4E9DCC1") .build(); private static final Operation INSERT_TEST2 = insertInto("test2") .columns("var1", "var2", "var3", "var4", "var5", "var6", "var7", "var8", "var9", "var10", "var11", "var12", "var13", "var14", "var15", "var16") .values(1, true, 2, 3, 4, 5.6, 7.8, Time.valueOf("09:46:30"), Date.valueOf("2014-05-24"), Timestamp.valueOf("2014-05-24 09:46:30"), new byte[0], "text", 5, 7, null, "30B443AE-C0C9-4790-9BEC-CE1380808435") .values(null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null) .build(); private static final Operation SQL = sql( "update test set var11 = FILE_READ('classpath:h2-logo-2.png') where var1 = 1", "update test set var11 = FILE_READ('classpath:logo-dev.jpg') where var1 = 10", "update test set var11 = FILE_READ('classpath:logo-dev.jpg') where var1 = 100", "update test set var11 = FILE_READ('classpath:logo-dev.jpg') where var1 = 1000", "update test2 set var11 = FILE_READ('classpath:h2-logo-2.png') where var1 = 1" ); private static final Operation OPERATIONS = sequenceOf(DELETE_ALL, INSERT_MOVIE, INSERT_ACTOR, INSERT_INTERPRETATION, INSERT_TEST, INSERT_TEST2, SQL); private static final DbSetup DB_SETUP = new DbSetup(new DriverManagerDestination("jdbc:h2:mem:test", "SA", ""), OPERATIONS); @Before public void initiate() throws IllegalArgumentException, IllegalAccessException, NoSuchFieldException, SecurityException { Field fieldLastSetup = DbSetupTracker.class.getDeclaredField("lastSetupLaunched"); Field fieldNextLaunch = DbSetupTracker.class.getDeclaredField("nextLaunchSkipped"); fieldLastSetup.setAccessible(true); fieldNextLaunch.setAccessible(true); Boolean nextLaunchSkipped = fieldNextLaunch.getBoolean(dbSetupTracker); DbSetup lastSetupLaunched = (DbSetup) fieldLastSetup.get(dbSetupTracker); boolean skipLaunch = nextLaunchSkipped && DB_SETUP.equals(lastSetupLaunched); LOG.warning("--------------------------------------------------"); LOG.warning(getClass().getCanonicalName() + " - " + testNameRule.getMethodName() + " - skipLaunch : " + skipLaunch + " (" + nextLaunchSkipped + " && " + DB_SETUP.equals(lastSetupLaunched) + ")"); LOG.warning("--------------------------------------------------"); dbSetupTracker.launchIfNecessary(DB_SETUP); } @After public void determineIfReloadIsNeeded() throws NoSuchMethodException, SecurityException { Annotation classAnnotation = getClass().getAnnotation(NeedReload.class); if (classAnnotation != null) { return; } String methodName = testNameRule.getMethodName(); Method method = getClass().getDeclaredMethod(methodName); Annotation methodAnnotation = method.getAnnotation(NeedReload.class); if (methodAnnotation != null) { return; } dbSetupTracker.skipNextLaunch(); } /** * Returns an instance of a {@code Column}. * * @param columnName The column name. * @param valuesList The values in the column. * @return An instance. * @throws Exception Exception */ protected static Column getColumn(String columnName, List<Value> valuesList) throws Exception { Constructor<Column> constructor = Column.class.getDeclaredConstructor(String.class, List.class, LetterCase.class); constructor.setAccessible(true); return constructor.newInstance(columnName, valuesList, LetterCase.COLUMN_DEFAULT); } /** * Returns an instance of a {@code Row}. * * @param pksNameList The list of the primary keys name. * @param columnsNameList The list of the columns name. * @param valuesList The values in the row. * @return An instance. * @throws Exception Exception */ protected static Row getRow(List<String> pksNameList, List<String> columnsNameList, List<Value> valuesList) throws Exception { Constructor<Row> constructor = Row.class.getDeclaredConstructor(List.class, List.class, List.class, LetterCase.class, LetterCase.class); constructor.setAccessible(true); return constructor.newInstance(pksNameList, columnsNameList, valuesList, LetterCase.COLUMN_DEFAULT, LetterCase.PRIMARY_KEY_DEFAULT); } /** * Returns an instance of a {@code Table}. * * @param columnsNameList The list of column name. * @param pksNameList The list of primary key. * @param rowsList The list of rows. * @return An instance. * @throws Exception Exception */ protected static Table getTable(List<String> columnsNameList, List<String> pksNameList, List<Row> rowsList) throws Exception { Constructor<Table> constructor = Table.class.getDeclaredConstructor(); constructor.setAccessible(true); Table table = constructor.newInstance(); Field field = AbstractDbData.class.getDeclaredField("dataType"); field.setAccessible(true); field.set(table, DataType.TABLE); Field field1 = AbstractDbData.class.getDeclaredField("columnsNameList"); field1.setAccessible(true); field1.set(table, columnsNameList); Field field2 = AbstractDbData.class.getDeclaredField("pksNameList"); field2.setAccessible(true); field2.set(table, pksNameList); Field field3 = AbstractDbData.class.getDeclaredField("rowsList"); field3.setAccessible(true); field3.set(table, rowsList); return table; } /** * Returns an instance of a {@code Request}. * * @param columnsNameList The list of column name. * @param pksNameList The list of primary key. * @param rowsList The list of rows. * @return An instance. * @throws Exception Exception */ protected static Request getRequest(List<String> columnsNameList, List<String> pksNameList, List<Row> rowsList) throws Exception { Constructor<Request> constructor = Request.class.getDeclaredConstructor(); constructor.setAccessible(true); Request request = constructor.newInstance(); Field field = AbstractDbData.class.getDeclaredField("dataType"); field.setAccessible(true); field.set(request, DataType.REQUEST); Field field1 = AbstractDbData.class.getDeclaredField("columnsNameList"); field1.setAccessible(true); field1.set(request, columnsNameList); Field field2 = AbstractDbData.class.getDeclaredField("pksNameList"); field2.setAccessible(true); field2.set(request, pksNameList); Field field3 = AbstractDbData.class.getDeclaredField("rowsList"); field3.setAccessible(true); field3.set(request, rowsList); return request; } /** * Returns an instance of a {@code Value}. * * @param columnName The name of the column. * @param object The object in the value. * @return An instance. * @throws Exception Exception */ protected static Value getValue(String columnName, Object object)throws Exception { Constructor<Value> constructor = Value.class.getDeclaredConstructor(String.class, Object.class, LetterCase.class); constructor.setAccessible(true); Value value = constructor.newInstance(columnName, object, LetterCase.COLUMN_DEFAULT); return value; } /** * Returns an instance of a {@code Changes}. * * @param changesList The list of changes. * @return An instance. * @throws Exception Exception */ protected static Changes getChanges(List<Change> changesList) throws Exception { Constructor<Changes> constructor = Changes.class.getDeclaredConstructor(); constructor.setAccessible(true); Changes changes = constructor.newInstance(); Field field = Changes.class.getDeclaredField("changesList"); field.setAccessible(true); field.set(changes, changesList); return changes; } /** * Returns an instance of a {@code Change}. * * @param dataType The type of the data on which is the change. * @param dataName The name of the data on which is the change. * @param changeType The type of the change. * @param rowAtStartPoint The row at start point. * @param rowAtEndPoint The row at end point. * @return An instance. * @throws Exception Exception */ protected static Change getChange(DataType dataType, String dataName, ChangeType changeType, Row rowAtStartPoint, Row rowAtEndPoint) throws Exception { Constructor<Change> constructor = Change.class .getDeclaredConstructor(DataType.class, String.class, ChangeType.class, Row.class, Row.class, LetterCase.class, LetterCase.class, LetterCase.class); constructor.setAccessible(true); return constructor.newInstance(dataType, dataName, changeType, rowAtStartPoint, rowAtEndPoint, LetterCase.TABLE_DEFAULT, LetterCase.COLUMN_DEFAULT, LetterCase.PRIMARY_KEY_DEFAULT); } /** * Returns an instance of a {@code Change} for creation on a table. * * @param dataName The name of the data on which is the change. * @param rowAtEndPoint The row at end point. * @return An instance. * @throws Exception Exception */ protected static Change getTableCreationChange(String dataName, Row rowAtEndPoint) throws Exception { return getChange(DataType.TABLE, dataName, ChangeType.CREATION, null, rowAtEndPoint); } /** * Returns an instance of a {@code Change} for creation on a table. * * @param dataName The name of the data on which is the change. * @param rowAtStartPoint The row at start point. * @param rowAtEndPoint The row at end point. * @return An instance. * @throws Exception Exception */ protected static Change getTableModificationChange(String dataName, Row rowAtStartPoint, Row rowAtEndPoint) throws Exception { return getChange(DataType.TABLE, dataName, ChangeType.MODIFICATION, rowAtStartPoint, rowAtEndPoint); } /** * Returns an instance of a {@code Change} for deletion on a table. * * @param dataName The name of the data on which is the change. * @param rowAtStartPoint The row at start point. * @return An instance. * @throws Exception Exception */ protected static Change getTableDeletionChange(String dataName, Row rowAtStartPoint) throws Exception { return getChange(DataType.TABLE, dataName, ChangeType.DELETION, rowAtStartPoint, null); } /** * Update the database. * * @param request Request to update. * @param parameters The parameters of the request. */ protected void update(String request, Object... parameters) { try (Connection connection = dataSource.getConnection()) { try (PreparedStatement statement = connection.prepareStatement(request)) { for (int i = 0; i < parameters.length; i++) { statement.setObject(i + 1, parameters[i]); } statement.executeUpdate(); } } catch (Exception e) { e.printStackTrace(); } } /** * Update the database for tests. */ protected void updateChangesForTests() { update("insert into movie values(4, 'Ghostbusters', 1984, '30B443AE-C0C9-4790-9BEC-CE1380808435')"); update("insert into actor values(4, 'Murray', 'Bill', PARSEDATETIME('21/09/1950', 'dd/MM/yyyy'), '30B443AE-C0C9-4790-9BEC-CE1380808435')"); update("insert into interpretation values(6, 4, 4, 'Dr Peter Venkman')"); update("delete from interpretation where id = 5"); update("delete from actor where id = 3"); update("update movie set title = 'The Avatar' where id = 3"); update("update actor set firstname = 'Susan Alexandra' where id = 1"); update("update interpretation set character = 'Doctor Grace Augustine' where id = 3"); } }