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
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
48 } else if (field.getName().equals("associatedWith")) {
49
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
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
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 }