View Javadoc
1   /*
2    *
3    * The DbUnit Database Testing Framework
4    * Copyright (C)2002-2024, DbUnit.org
5    *
6    * This library is free software; you can redistribute it and/or
7    * modify it under the terms of the GNU Lesser General Public
8    * License as published by the Free Software Foundation; either
9    * version 2.1 of the License, or (at your option) any later version.
10   *
11   * This library is distributed in the hope that it will be useful,
12   * but WITHOUT ANY WARRANTY; without even the implied warranty of
13   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14   * Lesser General License for more details.
15   *
16   * You should have received a copy of the GNU Lesser General Public
17   * License along with this library; if not, write to the Free Software
18   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19   *
20   */
21  
22  package org.dbunit.ant;
23  
24  import static org.assertj.core.api.Assertions.assertThat;
25  import static org.assertj.core.api.Assertions.assertThatCode;
26  import static org.assertj.core.api.Assertions.assertThatThrownBy;
27  import static org.assertj.core.api.Assertions.catchThrowable;
28  import static org.junit.jupiter.api.Assertions.assertEquals;
29  import static org.junit.jupiter.api.Assertions.assertNull;
30  import static org.junit.jupiter.api.Assertions.assertThrows;
31  import static org.junit.jupiter.api.Assertions.fail;
32  
33  import java.io.File;
34  import java.nio.charset.StandardCharsets;
35  import java.sql.SQLException;
36  import java.util.Hashtable;
37  import java.util.Iterator;
38  import java.util.List;
39  import org.apache.tools.ant.BuildException;
40  import org.apache.tools.ant.Target;
41  import org.apache.tools.ant.Task;
42  import org.apache.tools.ant.UnknownElement;
43  import org.dbunit.DatabaseEnvironment;
44  import org.dbunit.DatabaseUnitException;
45  import org.dbunit.IDatabaseTester;
46  import org.dbunit.ant.adapter.BuildFileExtension;
47  import org.dbunit.database.DatabaseConfig;
48  import org.dbunit.database.IDatabaseConnection;
49  import org.dbunit.dataset.FilteredDataSet;
50  import org.dbunit.dataset.IDataSet;
51  import org.dbunit.dataset.ITable;
52  import org.dbunit.dataset.NoSuchTableException;
53  import org.dbunit.dataset.datatype.IDataTypeFactory;
54  import org.dbunit.ext.mssql.InsertIdentityOperation;
55  import org.dbunit.ext.oracle.OracleDataTypeFactory;
56  import org.dbunit.operation.DatabaseOperation;
57  import org.dbunit.testutil.TestUtils;
58  import org.dbunit.util.FileHelper;
59  import org.junit.jupiter.api.AfterEach;
60  import org.junit.jupiter.api.BeforeAll;
61  import org.junit.jupiter.api.BeforeEach;
62  import org.junit.jupiter.api.Test;
63  import org.junit.jupiter.api.extension.RegisterExtension;
64  import org.slf4j.Logger;
65  import org.slf4j.LoggerFactory;
66  
67  /**
68   * Ant-based test class for the Dbunit ant task definition.
69   *
70   * @author Timothy Ruppert
71   * @author Ben Cox
72   * @author Last changed by: $Author$
73   * @version $Revision$ $Date$
74   * @since Jun 10, 2002
75   */
76  public class DbUnitTaskIT
77  {
78      private final Logger log = LoggerFactory.getLogger(getClass());
79  
80      @RegisterExtension
81      public BuildFileExtension rule = new BuildFileExtension();
82  
83      static protected Class classUnderTest = DbUnitTaskIT.class;
84  
85      private static final String BUILD_FILE_DIR = "xml";
86      private static final String OUTPUT_DIR = "target/xml";
87  
88      private File outputDir;
89  
90      @BeforeAll
91      public static void initializeDbEnvironment() throws Exception {
92          // This line ensure test database is initialized
93          DatabaseEnvironment.getInstance();
94      }
95  
96      @BeforeEach
97      public void setUp() throws Exception
98      {
99          final String filePath = BUILD_FILE_DIR + "/antTestBuildFile.xml";
100         assertThat(TestUtils.getFile(filePath)).as("Buildfile not found")
101         .isFile();
102         rule.configureProject(TestUtils.getFileName(filePath));
103         outputDir = rule.getProject().getBaseDir().toPath().resolve(OUTPUT_DIR).toFile();
104         outputDir.mkdirs();
105     }
106 
107     @AfterEach
108     public void tearDown() throws Exception
109     {
110         outputDir = rule.getProject().getBaseDir().toPath().resolve(OUTPUT_DIR).toFile();
111         FileHelper.deleteDirectory(outputDir);
112     }
113 
114     @Test
115     public void testNoDriver_withMissingDriverAttribute_throwsBuildException()
116     {
117         assertThrows(BuildException.class,
118                 () -> rule.executeTarget("no-driver"),
119                 "Should have required a driver attribute.");
120     }
121 
122     @Test
123     public void testNoDbUrl_withMissingUrlAttribute_throwsBuildException()
124     {
125         assertThrows(BuildException.class,
126                 () -> rule.executeTarget("no-db-url"),
127                 "Should have required a url attribute.");
128     }
129 
130     @Test
131     public void testNoUserid_withMissingUseridAttribute_throwsBuildException()
132     {
133         assertThrows(BuildException.class,
134                 () -> rule.executeTarget("no-userid"),
135                 "Should have required a userid attribute.");
136     }
137 
138     @Test
139     public void testNoPassword_withMissingPasswordAttribute_throwsBuildException()
140     {
141         assertThatThrownBy(() -> rule.executeTarget("no-password"))
142         .as("Should have required a password attribute.")
143         .isInstanceOf(BuildException.class);
144     }
145 
146     @Test
147     public void testInvalidDatabaseInformation_withBadConnectionInfo_causeIsSqlException()
148     {
149         final Throwable thrown =
150                 catchThrowable(() -> rule.executeTarget("invalid-db-info"));
151         assertThat(thrown.getCause()).as("Should have thrown a SQLException.")
152         .isNotNull().isInstanceOf(SQLException.class);
153 
154     }
155 
156     @Test
157     public void testInvalidOperationType_withUnknownType_causeIsIllegalArgumentException()
158     {
159         final Throwable thrown =
160                 catchThrowable(() -> rule.executeTarget("invalid-type"));
161         assertThat(thrown.getCause())
162         .as("Should have thrown an IllegalArgumentException.")
163         .isNotNull().isInstanceOf(IllegalArgumentException.class);
164     }
165 
166     @Test
167     public void testSetFlatFalse_withXmlFormatAttribute_returnsXmlFormat()
168     {
169         final String targetName = "set-format-xml";
170         final Operation operation =
171                 (Operation) getFirstStepFromTarget(targetName);
172         assertThat(operation.getFormat()).as(
173                 "Operation attribute format should have been 'xml', but was: "
174                         + operation.getFormat())
175         .isEqualTo("xml");
176 
177     }
178 
179     @Test
180     public void testResolveOperationTypes_withAllOperationTypeNames_resolvesCorrectOperations()
181     {
182         assertOperationType("Should have been a NONE operation",
183                 "test-type-none", DatabaseOperation.NONE);
184         assertOperationType("Should have been an DELETE_ALL operation",
185                 "test-type-delete-all", DatabaseOperation.DELETE_ALL);
186         assertOperationType("Should have been an INSERT operation",
187                 "test-type-insert", DatabaseOperation.INSERT);
188         assertOperationType("Should have been an UPDATE operation",
189                 "test-type-update", DatabaseOperation.UPDATE);
190         assertOperationType("Should have been an REFRESH operation",
191                 "test-type-refresh", DatabaseOperation.REFRESH);
192         assertOperationType("Should have been an CLEAN_INSERT operation",
193                 "test-type-clean-insert", DatabaseOperation.CLEAN_INSERT);
194         assertOperationType("Should have been an CLEAN_INSERT operation",
195                 "test-type-clean-insert-composite",
196                 DatabaseOperation.CLEAN_INSERT);
197         assertOperationType("Should have been an CLEAN_INSERT operation",
198                 "test-type-clean-insert-composite-combine",
199                 DatabaseOperation.CLEAN_INSERT);
200         assertOperationType("Should have been an DELETE operation",
201                 "test-type-delete", DatabaseOperation.DELETE);
202         assertOperationType("Should have been an MSSQL_INSERT operation",
203                 "test-type-mssql-insert", InsertIdentityOperation.INSERT);
204         assertOperationType("Should have been an MSSQL_REFRESH operation",
205                 "test-type-mssql-refresh", InsertIdentityOperation.REFRESH);
206         assertOperationType("Should have been an MSSQL_CLEAN_INSERT operation",
207                 "test-type-mssql-clean-insert",
208                 InsertIdentityOperation.CLEAN_INSERT);
209     }
210 
211     @Test
212     public void testInvalidCompositeOperationSrc_withNestedSrcAttribute_throwsBuildException()
213     {
214         expectBuildException("invalid-composite-operation-src",
215                 "Should have objected to nested operation src attribute "
216                         + "being set.");
217     }
218 
219     @Test
220     public void testInvalidCompositeOperationFlat_withNestedFormatAttribute_throwsBuildException()
221     {
222         expectBuildException("invalid-composite-operation-format-flat",
223                 "Should have objected to nested operation format attribute "
224                         + "being set.");
225     }
226 
227     @Test
228     public void testExportFull_withFullExportTarget_returnsFlatFormatEmptyTableList()
229     {
230         final String targetName = "test-export-full";
231         final Export export = (Export) getFirstStepFromTarget(targetName);
232         assertThat(export.getFormat()).as("Should have been a flat format, "
233                 + "but was: " + export.getFormat())
234         .isEqualToIgnoringCase("flat");
235 
236         final List tables = export.getTables();
237         assertThat(tables)
238         .as("Should have been an empty table list "
239                 + "(indicating a full dataset), but was: " + tables)
240         .isEmpty();
241 
242     }
243 
244     @Test
245     public void testExportPartial_withTwoTableExport_returnsTwoTableNames()
246     {
247         final String targetName = "test-export-partial";
248         final Export export = (Export) getFirstStepFromTarget(targetName);
249         final List tables = export.getTables();
250         assertThat(tables).as("table count").hasSize(2);
251 
252         final Table testTable = (Table) tables.get(0);
253         final Table pkTable = (Table) tables.get(1);
254         assertThat(testTable.getName())
255         .as("Should have been been TABLE TEST_TABLE, but was: "
256                 + testTable.getName())
257         .isEqualTo("TEST_TABLE");
258         assertThat(pkTable.getName())
259         .as("Should have been been TABLE PK_TABLE, but was: "
260                 + pkTable.getName())
261         .isEqualTo("PK_TABLE");
262 
263     }
264 
265     @Test
266     public void testExportWithForwardOnlyResultSetTable_withForwardOnlyConfig_setsForwardOnlyFactory()
267             throws SQLException, DatabaseUnitException
268     {
269         final String targetName =
270                 "test-export-forward-only-result-set-table-via-config";
271 
272         // Test if the correct result set table factory is set according to
273         // dbconfig
274         final Export export = (Export) getFirstStepFromTarget(targetName);
275         final DbUnitTask task = getFirstTargetTask(targetName);
276         final IDatabaseConnection connection = task.createConnection();
277         final IDataSet dataSetToBeExported =
278                 export.getExportDataSet(connection);
279         assertThat(connection.getConfig()
280                 .getProperty(
281                         DatabaseConfig.PROPERTY_RESULTSET_TABLE_FACTORY)
282                 .getClass().getName()).isEqualTo("org.dbunit.database.ForwardOnlyResultSetTableFactory");
283 
284     }
285 
286     @Test
287     public void testExportFlat_withFlatFormatTarget_returnsFlatFormat()
288     {
289         final String targetName = "test-export-format-flat";
290         final Export export = (Export) getFirstStepFromTarget(targetName);
291         assertThat(export.getFormat()).as("format").isEqualTo("flat");
292     }
293 
294     @Test
295     public void testExportFlatWithDocytpe_withDoctypeSet_returnsFlatFormatAndDoctype()
296     {
297         final String targetName = "test-export-format-flat-with-doctype";
298         final Export export = (Export) getFirstStepFromTarget(targetName);
299         assertThat(export.getFormat()).as("format").isEqualTo("flat");
300         assertThat(export.getDoctype()).as("doctype").isEqualTo("dataset.dtd");
301     }
302 
303     @Test
304     public void testExportFlatWithEncoding_withEncodingSet_returnsFlatFormatAndIso8859Encoding()
305     {
306         final String targetName = "test-export-format-flat-with-encoding";
307         final Export export = (Export) getFirstStepFromTarget(targetName);
308         assertThat(export.getFormat()).as("format").isEqualTo("flat");
309         assertThat(export.getEncoding()).as("encoding").isEqualTo(StandardCharsets.ISO_8859_1);
310     }
311 
312     @Test
313     public void testExportXml_withXmlFormatTarget_returnsXmlFormat()
314     {
315         final String targetName = "test-export-format-xml";
316         final Export export = (Export) getFirstStepFromTarget(targetName);
317         assertThat(export.getFormat()).as("Should have been an xml format, "
318                 + "but was: " + export.getFormat())
319         .isEqualToIgnoringCase("xml");
320     }
321 
322     @Test
323     public void testExportCsv_withCsvFormatTarget_returnsCsvFormat()
324     {
325         final String targetName = "test-export-format-csv";
326         final Export export = (Export) getFirstStepFromTarget(targetName);
327         assertThat(export.getFormat()).as("Should have been a csv format, "
328                 + "but was: " + export.getFormat())
329         .isEqualToIgnoringCase("csv");
330     }
331 
332     @Test
333     public void testExportDtd_withDtdFormatTarget_returnsDtdFormat()
334     {
335         final String targetName = "test-export-format-dtd";
336         final Export export = (Export) getFirstStepFromTarget(targetName);
337         assertThat(export.getFormat()).as("Should have been a dtd format, "
338                 + "but was: " + export.getFormat())
339         .isEqualToIgnoringCase("dtd");
340     }
341 
342     @Test
343     public void testInvalidExportFormat_withInvalidFormatAttribute_throwsBuildException()
344     {
345         expectBuildException("invalid-export-format",
346                 "Should have objected to invalid format attribute.");
347     }
348 
349     @Test
350     public void testExportXmlOrdered_withOrderedXmlExport_returnsFilteredDataSet() throws Exception
351     {
352         final String targetName = "test-export-format-xml-ordered";
353         final Export export = (Export) getFirstStepFromTarget(targetName);
354         assertThat(export.isOrdered()).as("Should be ordered").isTrue();
355         assertThat(export.getFormat()).as("Should have been an xml format, "
356                 + "but was: " + export.getFormat()).isEqualTo("xml");
357 
358         // Test if the correct dataset is created for ordered export
359         final DbUnitTask task = getFirstTargetTask(targetName);
360         final IDatabaseConnection connection = task.createConnection();
361         final IDataSet dataSetToBeExported =
362                 export.getExportDataSet(connection);
363         // Ordered export should use the filtered dataset
364         assertEquals(dataSetToBeExported.getClass(), FilteredDataSet.class);
365     }
366 
367     @Test
368     public void testExportQuery_withQueryExportTarget_returnsQueriesWithSql()
369     {
370         final String targetName = "test-export-query";
371         final Export export = (Export) getFirstStepFromTarget(targetName);
372         assertThat(export.getFormat()).as("format").isEqualTo("flat");
373 
374         final List queries = export.getTables();
375         assertThat(getQueryCount(queries)).as("query count").isEqualTo(2);
376 
377         final Query testTable = (Query) queries.get(0);
378         assertThat(testTable.getName()).as("name").isEqualTo("TEST_TABLE");
379         assertThat(testTable.getSql()).as("sql")
380         .isEqualTo("SELECT * FROM TEST_TABLE ORDER BY column0 DESC");
381 
382         final Query pkTable = (Query) queries.get(1);
383         assertThat(pkTable.getName()).as("name").isEqualTo("PK_TABLE");
384         assertThat(pkTable.getSql()).as("sql")
385         .isEqualTo("SELECT * FROM PK_TABLE");
386     }
387 
388     @Test
389     public void testExportWithQuerySet_withQuerySetTarget_returnsQuerySetsTablesAndQueries()
390     {
391         final String targetName = "test-export-with-queryset";
392         final Export export = (Export) getFirstStepFromTarget(targetName);
393         assertThat(export.getFormat()).as("format").isEqualTo("csv");
394 
395         final List queries = export.getTables();
396 
397         assertThat(getQueryCount(queries)).as("query count").isEqualTo(1);
398         assertThat(getTableCount(queries)).as("table count").isEqualTo(1);
399         assertThat(getQuerySetCount(queries)).as("queryset count").isEqualTo(2);
400 
401         final Query secondTable = (Query) queries.get(0);
402         assertThat(secondTable.getName()).as("name").isEqualTo("SECOND_TABLE");
403         assertThat(secondTable.getSql()).as("sql")
404         .isEqualTo("SELECT * FROM SECOND_TABLE");
405 
406         final QuerySet queryset1 = (QuerySet) queries.get(1);
407 
408         final Query testTable = (Query) queryset1.getQueries().get(0);
409 
410         assertThat(testTable.getName()).as("name").isEqualTo("TEST_TABLE");
411 
412         final QuerySet queryset2 = (QuerySet) queries.get(2);
413 
414         final Query pkTable = (Query) queryset2.getQueries().get(0);
415         final Query testTable2 = (Query) queryset2.getQueries().get(1);
416 
417         assertThat(pkTable.getName()).as("name").isEqualTo("PK_TABLE");
418         assertThat(testTable2.getName()).as("name").isEqualTo("TEST_TABLE");
419 
420         final Table emptyTable = (Table) queries.get(3);
421 
422         assertThat(emptyTable.getName()).as("name").isEqualTo("EMPTY_TABLE");
423     }
424 
425     @Test
426     public void testWithQuerySetIdAndRefid_withBothAttributesSet_resolvesRefidWithoutThrowing()
427     {
428         assertThatCode(() -> rule.executeTarget("invalid-queryset"))
429                 .as("Ant resolves the refid attribute first and no longer errors on the id/refid combination.")
430                 .doesNotThrowAnyException();
431     }
432 
433     @Test
434     public void testWithReferenceQuerySet_withRefidQuerySet_returnsQueriesFromReference()
435     {
436         final String targetName = "test-queryset-reference";
437 
438         final Export export = (Export) getFirstStepFromTarget(targetName);
439 
440         final List tables = export.getTables();
441 
442         assertThat(tables).as("total count").hasSize(1);
443 
444         final QuerySet queryset = (QuerySet) tables.get(0);
445         final Query testTable = (Query) queryset.getQueries().get(0);
446         final Query secondTable = (Query) queryset.getQueries().get(1);
447 
448         assertThat(testTable.getName()).as("name").isEqualTo("TEST_TABLE");
449         assertThat(testTable.getSql())
450         .as("sql").isEqualTo("SELECT * FROM TEST_TABLE WHERE COLUMN0 = 'row0 col0'");
451 
452         assertThat(secondTable.getName()).as("name").isEqualTo("SECOND_TABLE");
453         assertThat(secondTable.getSql())
454         .as("sql").isEqualTo("SELECT B.* FROM TEST_TABLE A, SECOND_TABLE B "
455                 + "WHERE A.COLUMN0 = 'row0 col0' AND B.COLUMN0 = A.COLUMN0");
456 
457     }
458 
459     @Test
460     public void testExportQueryMixed_withMixedTableAndQueryExport_returnsBothTypes()
461     {
462         final String targetName = "test-export-query-mixed";
463         final Export export = (Export) getFirstStepFromTarget(targetName);
464         assertThat(export.getFormat()).as("format").isEqualTo("flat");
465 
466         final List tables = export.getTables();
467         assertThat(tables).as("total count").hasSize(2);
468         assertThat(getTableCount(tables)).as("table count").isEqualTo(1);
469         assertThat(getQueryCount(tables)).as("query count").isEqualTo(1);
470 
471         final Table testTable = (Table) tables.get(0);
472         assertThat(testTable.getName()).as("name").isEqualTo("TEST_TABLE");
473 
474         final Query pkTable = (Query) tables.get(1);
475         assertThat(pkTable.getName()).as("name").isEqualTo("PK_TABLE");
476     }
477 
478     /**
479      * Tests the exception that is thrown when the compare fails because the
480      * source format was different from the previous "export" task's write
481      * format.
482      */
483     @Test
484     public void testExportAndCompareFormatMismatch_withMismatchedFormats_throwsDatabaseUnitException()
485     {
486         final String targetName = "test-export-and-compare-format-mismatch";
487 
488         try
489         {
490             getFirstTargetTask(targetName);
491             fail("Should not be able to invoke ant task where the expected table was not found because it was tried to read in the wrong format.");
492         } catch (final BuildException expected)
493         {
494             final Throwable cause = expected.getCause();
495             assertThat(cause).isInstanceOf(DatabaseUnitException.class);
496             final DatabaseUnitException dbUnitException =
497                     (DatabaseUnitException) cause;
498             final String filename =
499                     outputDir.toPath().resolve("antExportDataSet.xml").toString();
500             final String expectedMsg = "Did not find table in source file '"
501                     + filename + "' using format 'xml'";
502             assertThat(dbUnitException.getMessage()).isEqualTo(expectedMsg);
503             assertThat(dbUnitException.getCause())
504             .isInstanceOf(NoSuchTableException.class);
505             final NoSuchTableException nstException =
506                     (NoSuchTableException) dbUnitException.getCause();
507             assertThat(nstException.getMessage()).isEqualTo("TEST_TABLE");
508         }
509     }
510 
511     @Test
512     public void testDataTypeFactory_withOracleDataTypeFactory_setsOracleFactory() throws Exception
513     {
514         final String targetName = "test-datatypefactory";
515         final DbUnitTask task = getFirstTargetTask(targetName);
516 
517         final IDatabaseConnection connection = task.createConnection();
518         final IDataTypeFactory factory =
519                 (IDataTypeFactory) connection.getConfig()
520                 .getProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY);
521 
522         final Class expectedClass = OracleDataTypeFactory.class;
523         assertThat(factory.getClass()).as("factory").isEqualTo(expectedClass);
524     }
525 
526     @Test
527     public void testEscapePattern_withEscapePatternTarget_setsEscapePattern() throws Exception
528     {
529         final String targetName = "test-escapepattern";
530         final DbUnitTask task = getFirstTargetTask(targetName);
531 
532         final IDatabaseConnection connection = task.createConnection();
533         final String actualPattern = (String) connection.getConfig()
534                 .getProperty(DatabaseConfig.PROPERTY_ESCAPE_PATTERN);
535 
536         final String expectedPattern = "[?]";
537         assertThat(expectedPattern).as("factory").isEqualTo(actualPattern);
538     }
539 
540     @Test
541     public void testDataTypeFactoryViaGenericConfig_withGenericConfigTarget_setsFactoryAndProperties() throws Exception
542     {
543         final String targetName = "test-datatypefactory-via-generic-config";
544         final DbUnitTask task = getFirstTargetTask(targetName);
545 
546         final IDatabaseConnection connection = task.createConnection();
547 
548         final DatabaseConfig config = connection.getConfig();
549 
550         final IDataTypeFactory factory = (IDataTypeFactory) config
551                 .getProperty(DatabaseConfig.PROPERTY_DATATYPE_FACTORY);
552         final Class expectedClass = OracleDataTypeFactory.class;
553         assertThat(factory.getClass()).as("factory").isEqualTo(expectedClass);
554 
555         final String[] actualTableType = (String[]) config
556                 .getProperty(DatabaseConfig.PROPERTY_TABLE_TYPE);
557         assertThat(actualTableType).as("tableType")
558         .isEqualTo(new String[] {"TABLE", "SYNONYM"});
559         assertThat(connection.getConfig()
560                 .getFeature(DatabaseConfig.FEATURE_BATCHED_STATEMENTS))
561         .as("batched statements feature should be true")
562         .isTrue();
563         assertThat(connection.getConfig()
564                 .getFeature(DatabaseConfig.FEATURE_CASE_SENSITIVE_TABLE_NAMES))
565         .as("qualified tablenames feature should be true")
566         .isTrue();
567     }
568 
569     @Test
570     public void testClasspath_withInvalidUrl_throwsBuildExceptionWithSqlCause() throws Exception
571     {
572         final String targetName = "test-classpath";
573 
574         try
575         {
576             rule.executeTarget(targetName);
577             fail("Should not be able to connect with invalid url!");
578         } catch (final BuildException e)
579         {
580             // Verify exception type
581             assertThat(e.getCause()).isInstanceOf(SQLException.class);
582         }
583 
584     }
585 
586     @Test
587     public void testDriverNotInClasspath_withDriverAbsent_throwsBuildExceptionWithClassNotFoundCause() throws Exception
588     {
589         final String targetName = "test-drivernotinclasspath";
590 
591         try
592         {
593             rule.executeTarget(targetName);
594             fail("Should not have found driver!");
595         } catch (final BuildException e)
596         {
597             // Verify exception type
598             assertThat(e.getCause()).as("nested exception type")
599             .isInstanceOf(ClassNotFoundException.class);
600         }
601     }
602 
603     @Test
604     public void testReplaceOperation_withReplaceTarget_updatesFirstRowToNull() throws Exception
605     {
606         final String targetName = "test-replace";
607         final IDatabaseTester dbTest =
608                 DatabaseEnvironment.getInstance().getDatabaseTester();
609         rule.executeTarget(targetName);
610         final IDataSet ds = dbTest.getConnection().createDataSet();
611         final ITable table = ds.getTable("PK_TABLE");
612         assertThat(table.getValue(0, "NORMAL0")).isNull();
613         assertThat(table.getValue(1, "NORMAL0")).isEqualTo("row 1");
614     }
615 
616     @Test
617     public void testOrderedOperation_withOrderedTarget_insertsRowsInOrder() throws Exception
618     {
619         final String targetName = "test-ordered";
620         final IDatabaseTester dbTest =
621                 DatabaseEnvironment.getInstance().getDatabaseTester();
622         rule.executeTarget(targetName);
623         final IDataSet ds = dbTest.getConnection().createDataSet();
624         final ITable table = ds.getTable("PK_TABLE");
625         assertEquals("row 0", table.getValue(0, "NORMAL0"));
626         assertEquals("row 1", table.getValue(1, "NORMAL0"));
627     }
628 
629     @Test
630     public void testReplaceOrderedOperation_withReplaceOrderedTarget_updatesFirstRowToNull() throws Exception
631     {
632         final String targetName = "test-replace-ordered";
633         final IDatabaseTester dbTest =
634                 DatabaseEnvironment.getInstance().getDatabaseTester();
635         rule.executeTarget(targetName);
636         final IDataSet ds = dbTest.getConnection().createDataSet();
637         final ITable table = ds.getTable("PK_TABLE");
638         assertNull(table.getValue(0, "NORMAL0"));
639         assertEquals("row 1", table.getValue(1, "NORMAL0"));
640     }
641 
642     protected void assertOperationType(final String failMessage,
643             final String targetName, final DatabaseOperation expected)
644     {
645         final Operation oper = (Operation) getFirstStepFromTarget(targetName);
646         final DatabaseOperation dbOper = oper.getDbOperation();
647         assertThat(dbOper).as(failMessage + ", but was: " + dbOper)
648         .isEqualTo(expected);
649     }
650 
651     protected int getQueryCount(final List tables)
652     {
653         int count = 0;
654         for (final Iterator it = tables.iterator(); it.hasNext();)
655         {
656             if (it.next() instanceof Query)
657             {
658                 count++;
659             }
660         }
661 
662         return count;
663     }
664 
665     protected int getTableCount(final List tables)
666     {
667         int count = 0;
668         for (final Iterator it = tables.iterator(); it.hasNext();)
669         {
670             if (it.next() instanceof Table)
671             {
672                 count++;
673             }
674         }
675 
676         return count;
677     }
678 
679     protected int getQuerySetCount(final List tables)
680     {
681         int count = 0;
682         for (final Iterator it = tables.iterator(); it.hasNext();)
683         {
684             if (it.next() instanceof QuerySet)
685             {
686                 count++;
687             }
688         }
689 
690         return count;
691     }
692 
693     protected DbUnitTaskStep getFirstStepFromTarget(final String targetName)
694     {
695         return getStepFromTarget(targetName, 0);
696     }
697 
698     protected DbUnitTaskStep getStepFromTarget(final String targetName,
699             final int index)
700     {
701         final DbUnitTask task = getFirstTargetTask(targetName);
702         final List steps = task.getSteps();
703         if (steps == null || steps.size() == 0)
704         {
705             fail("Can't get a dbunit <step> from the target: " + targetName
706                     + ". No steps available.");
707         }
708 
709         return (DbUnitTaskStep) steps.get(index);
710     }
711 
712     private DbUnitTask getFirstTargetTask(final String targetName)
713     {
714         final Hashtable targets = rule.getProject().getTargets();
715         rule.executeTarget(targetName);
716         final Target target = (Target) targets.get(targetName);
717 
718         DbUnitTask task = null;
719 
720         final Object[] tasks = target.getTasks();
721         // See https://ant.apache.org/faq.html#unknownelement.taskcontainer for
722         // this change
723         for (int i = 0; i < tasks.length; i++)
724         {
725             if (tasks[i] instanceof UnknownElement)
726             {
727                 ((UnknownElement) tasks[i]).maybeConfigure();
728                 final Task elm = ((UnknownElement) tasks[i]).getTask();
729                 if (elm instanceof DbUnitTask)
730                 {
731                     task = (DbUnitTask) elm;
732                     task.getSteps().forEach(s -> {
733                         try
734                         {
735                             ((DbUnitTaskStep)s).execute(((DbUnitTask) elm).createConnection());
736                         } catch (DatabaseUnitException | SQLException e)
737                         {
738                             log.error("getFirstTargetTask: Error creating connection", e);
739                         }
740                     });
741                 }
742             }
743         }
744 
745         return task;
746     }
747 
748     /**
749      * Runs a target, wait for a build exception.
750      *
751      * @param target
752      *            target to run
753      * @param cause
754      *            information string to reader of report
755      * @param msg
756      *            the message value of the build exception we are waiting for
757      *            set to null for any build exception to be valid
758      */
759     public void expectSpecificBuildException(final String target,
760             final String cause, final String msg)
761     {
762         try
763         {
764             rule.executeTarget(target);
765 
766         } catch (final BuildException ex)
767         {
768 
769             assertThat(ex.getMessage())
770             .as("Should throw BuildException because '" + cause
771                     + "' with message '" + msg + "' (actual message '"
772                     + ex.getMessage() + "' instead)")
773             .satisfiesAnyOf(check -> assertThat(msg).isNull(),
774                     check -> assertThat(check).isNotNull()
775                     .isEqualTo(msg));
776 
777             return;
778         }
779         fail("Should throw BuildException because: " + cause);
780     }
781 
782     public void expectBuildException(final String target, final String cause)
783     {
784         expectSpecificBuildException(target, cause, null);
785     }
786 }