View Javadoc

1   package liquibase.change;
2   
3   import liquibase.database.Database;
4   import liquibase.exception.ValidationErrors;
5   import liquibase.serializer.core.string.StringChangeLogSerializer;
6   import liquibase.statement.SqlStatement;
7   import liquibase.statement.DatabaseFunction;
8   import liquibase.test.TestContext;
9   import static org.junit.Assert.*;
10  import org.junit.Test;
11  
12  import java.lang.reflect.Field;
13  import java.lang.reflect.InvocationTargetException;
14  import java.math.BigInteger;
15  import java.util.*;
16  
17  /**
18   * Base test class for changes
19   */
20  public abstract class AbstractChangeTest {
21  
22      @Test
23      public abstract void getRefactoringName() throws Exception;
24  
25      @Test
26      public abstract void generateStatement() throws Exception;
27  
28      @Test
29      public abstract void getConfirmationMessage() throws Exception;
30  
31      @Test
32      public void generateCheckSum() throws Exception {
33          Change change = createClassUnderTest();
34          if (change == null) {
35              return;
36          }
37  
38          CheckSum checkSum = change.generateCheckSum();
39          assertNotNull(checkSum);
40          assertEquals(CheckSum.getCurrentVersion(), checkSum.getVersion());
41          assertTrue(checkSum.toString().startsWith(CheckSum.getCurrentVersion()+":"));
42  
43          Map<String, String> seenCheckSums = new HashMap<String, String>();
44          for (Field field : change.getClass().getDeclaredFields()) {
45              field.setAccessible(true);
46              if (field.getName().startsWith("$VR") || field.getName().equals("serialVersionUID")) {
47                  //code coverage related
48              } else if (field.getName().equals("associatedWith")) {
49                  //currently not used
50              } else  if (String.class.isAssignableFrom(field.getType())) {
51                  field.set(change, "asdghasdgasdg");
52                  checkThatChecksumIsNew(change, seenCheckSums, field);
53                  field.set(change, "gsgasdgasdggasdg sdg a");
54                  checkThatChecksumIsNew(change, seenCheckSums, field);
55              } else if (Boolean.class.isAssignableFrom(field.getType())) {
56                  field.set(change, Boolean.TRUE);
57                  checkThatChecksumIsNew(change, seenCheckSums, field);
58                  field.set(change, Boolean.FALSE);
59                  checkThatChecksumIsNew(change, seenCheckSums, field);
60              } else if (Integer.class.isAssignableFrom(field.getType())) {
61                  field.set(change, 6532);
62                  checkThatChecksumIsNew(change, seenCheckSums, field);
63                  field.set(change, -52352);
64                  checkThatChecksumIsNew(change, seenCheckSums, field);
65              } else if (DatabaseFunction.class.isAssignableFrom(field.getType())) {
66                  field.set(change, new DatabaseFunction("FUNC1"));
67                  checkThatChecksumIsNew(change, seenCheckSums, field);
68                  field.set(change, new DatabaseFunction("FUNC 2"));
69                  checkThatChecksumIsNew(change, seenCheckSums, field);
70              } else if (BigInteger.class.isAssignableFrom(field.getType())) {
71                  field.set(change, new BigInteger("6532"));
72                  checkThatChecksumIsNew(change, seenCheckSums, field);
73                  field.set(change, new BigInteger("-52352"));
74                  checkThatChecksumIsNew(change, seenCheckSums, field);
75              } else if (List.class.isAssignableFrom(field.getType())) {
76                  ColumnConfig column1 = new ColumnConfig();
77                  ((List) field.get(change)).add(column1);
78  
79                  column1.setName("ghsdgasdg");
80                  checkThatChecksumIsNew(change, seenCheckSums, field);
81  
82                  column1.setType("gasdgasdg");
83                  checkThatChecksumIsNew(change, seenCheckSums, field);
84  
85                  ColumnConfig column2 = new ColumnConfig();
86                  ((List) field.get(change)).add(column2);
87  
88                  column2.setName("87682346asgasdg");
89                  checkThatChecksumIsNew(change, seenCheckSums, field);
90  
91              } else {
92                  throw new RuntimeException("Unknown field type: "+field.getType()+" for "+field.getName());
93              }
94          }
95      }
96  
97      private Change createClassUnderTest() throws InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, ClassNotFoundException {
98          String className = getClass().getName().replaceAll("Test$", "");
99          if (className.indexOf("Abstract") > 0) {
100             return null;
101         }
102 
103         return (Change) Class.forName(className).getConstructor().newInstance();
104     }
105 
106     protected void checkThatChecksumIsNew(Change change, Map<String, String> seenCheckSums, Field field) {
107         String serialized = new StringChangeLogSerializer().serialize(change);
108 
109         CheckSum newCheckSum = change.generateCheckSum();
110         if (seenCheckSums.containsKey(newCheckSum.toString())) {
111             fail("generated duplicate checksum channging "+field.getName()+"\n"+serialized+"\nmatches\n"+seenCheckSums.get(newCheckSum.toString()));
112         }
113         seenCheckSums.put(newCheckSum.toString(), serialized);
114     }
115 
116 //    @Test
117 //    public void saveStatement() throws Exception {
118 //        Change change = new AbstractChange("test", "Test Refactoring", ChangeMetaData.PRIORITY_DEFAULT) {
119 //            public SqlStatement[] generateStatements(Database database) {
120 //                return new SqlStatement[]{new RawSqlStatement("GENERATED STATEMENT")};
121 //            }
122 //
123 //            public String getConfirmationMessage() {
124 //                return null;
125 //            }
126 //        };
127 //
128 //        StringWriter stringWriter = new StringWriter();
129 //
130 //        OracleDatabase database = new OracleDatabase();
131 //        database.saveStatements(change, new ArrayList<SqlVisitor>(), stringWriter);
132 //
133 //        assertEquals("GENERATED STATEMENT;" + StreamUtil.getLineSeparator() + StreamUtil.getLineSeparator(), stringWriter.getBuffer().toString());
134 //    }
135 
136 //todo: reintroduce    @Test
137 //    public void executeStatement() throws Exception {
138 //        Change change = new AbstractChange("test", "Test Refactorign", ChangeMetaData.PRIORITY_DEFAULT) {
139 //            public SqlStatement[] generateStatements(Database database) {
140 //                return new SqlStatement[]{new RawSqlStatement("GENERATED STATEMENT;")};
141 //            }
142 //
143 //            public String getConfirmationMessage() {
144 //                return null;
145 //            }
146 //        };
147 //
148 //        DatabaseConnection conn = createMock(DatabaseConnection.class);
149 ////        Statement statement = createMock(Statement.class);
150 //        conn.setAutoCommit(false);
151 ////        expect(((JdbcConnection) conn).getUnderlyingConnection().createStatement()).andReturn(statement);
152 //
153 ////        expect(statement.execute("GENERATED STATEMENT;")).andStubReturn(true);
154 ////        statement.close();
155 ////        expectLastCall();
156 //        replay(conn);
157 ////        replay(statement);
158 //
159 //        OracleDatabase database = new OracleDatabase();
160 //        database.setConnection(conn);
161 //
162 //        database.executeStatements(change, new ArrayList<SqlVisitor>());
163 //
164 //        verify(conn);
165 ////        verify(statement);
166 //    }
167 
168     protected void testChangeOnAllExcept(Change change, GenerateAllValidator validator, Class<? extends Database>... databases) throws Exception {
169         List<Class<? extends Database>> databsesToRun = new ArrayList<Class<? extends Database>>();
170         for (Database database : TestContext.getInstance().getAllDatabases()) {
171             List<Class<? extends Database>> databaseClasses = Arrays.asList(databases);
172             if (!databaseClasses.contains(database.getClass())) {
173                 databsesToRun.add(database.getClass());
174             }
175         }
176 
177         testChange(change, validator, databsesToRun.toArray(new Class[databsesToRun.size()]));
178     }
179 
180     protected void testChangeOnAll(Change change, GenerateAllValidator validator) throws Exception {
181         for (Database database : TestContext.getInstance().getAllDatabases()) {
182             SqlStatement[] sqlStatements = change.generateStatements(database);
183             try {
184                 validator.validate(sqlStatements, database);
185             } catch (AssertionError e) {
186                 AssertionError error = new AssertionError("GenerateAllValidator failed for " + database.getTypeName() + ": " + e.getMessage());
187                 error.setStackTrace(e.getStackTrace());
188 
189                 throw error;
190             }
191         }
192     }
193 
194     protected void testChange(Change change, GenerateAllValidator validator, Class<? extends Database>... databases) throws Exception {
195         for (Database database : TestContext.getInstance().getAllDatabases()) {
196             List<Class<? extends Database>> databaseClasses = Arrays.asList(databases);
197             if (!databaseClasses.contains(database.getClass())) {
198                 continue;
199             }
200 
201             SqlStatement[] sqlStatements = change.generateStatements(database);
202             try {
203                 validator.validate(sqlStatements, database);
204             } catch (AssertionError e) {
205                 AssertionError error = new AssertionError("GenerateAllValidator failed for " + database.getTypeName() + ": " + e.getMessage());
206                 error.setStackTrace(e.getStackTrace());
207 
208                 throw error;
209             }
210         }
211     }
212 
213     protected void testInverseOnAllExcept(AbstractChange change, InverseValidator validator, Class<? extends Database>... databases) throws Exception {
214         List<Class<? extends Database>> databsesToRun = new ArrayList<Class<? extends Database>>();
215         for (Database database : TestContext.getInstance().getAllDatabases()) {
216             List<Class<? extends Database>> databaseClasses = Arrays.asList(databases);
217             if (!databaseClasses.contains(database.getClass())) {
218                 databsesToRun.add(database.getClass());
219             }
220         }
221 
222         testInverse(change, validator, databsesToRun.toArray(new Class[databsesToRun.size()]));
223     }
224 
225     protected void testInverseOnAll(AbstractChange change, InverseValidator validator) throws Exception {
226         for (Database database : TestContext.getInstance().getAllDatabases()) {
227             Change[] inverses = change.createInverses();
228             try {
229                 validator.validate(inverses);
230             } catch (AssertionError e) {
231                 AssertionError error = new AssertionError("InverseValidator failed for " + database.getTypeName() + ": " + e.getMessage());
232                 error.setStackTrace(e.getStackTrace());
233 
234                 throw error;
235             }
236         }
237     }
238 
239     protected void testInverse(AbstractChange change, InverseValidator validator, Class<? extends Database>... databases) throws Exception {
240         for (Database database : TestContext.getInstance().getAllDatabases()) {
241             List<Class<? extends Database>> databaseClasses = Arrays.asList(databases);
242             if (!databaseClasses.contains(database.getClass())) {
243                 continue;
244             }
245 
246             Change[] inverses = change.createInverses();
247             try {
248                 validator.validate(inverses);
249             } catch (AssertionError e) {
250                 AssertionError error = new AssertionError("InverseValidator failed for " + database.getTypeName() + ": " + e.getMessage());
251                 error.setStackTrace(e.getStackTrace());
252 
253                 throw error;
254             }
255         }
256     }
257 
258     @Test
259     public void isSupported() throws Exception {
260         Change change = createClassUnderTest();
261         if (change == null) {
262             return;
263         }
264         for (Database database : TestContext.getInstance().getAllDatabases()) {
265             assertEquals("Unexpected availablity on "+database.getTypeName(), !changeIsUnsupported(database), change.supports(database));
266         }
267     }
268 
269     protected boolean changeIsUnsupported(Database database) {
270         return false;
271     }
272 
273     @Test
274     public void validate() throws Exception {
275         Change change = createClassUnderTest();
276         if (change == null) {
277             return;
278         }
279         for (Database database : TestContext.getInstance().getAllDatabases()) {
280             if (change.supports(database)) {
281                 ValidationErrors validationErrors = change.validate(database);
282                 assertTrue("no errors found for "+database.getClass().getName(), validationErrors.hasErrors());
283             }
284         }
285     }
286 
287     protected static interface GenerateAllValidator {
288         public void validate(SqlStatement[] statements, Database database);
289     }
290 
291     protected static interface InverseValidator {
292         public void validate(Change[] statements);
293     }
294 
295 }