diff --git a/pom.xml b/pom.xml
index 6639f5f..50d015e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -29,6 +29,14 @@
com.oracle.jdbc
ucp
+
+ com.oracle.jdbc
+ ojdbc8
+
+
+ com.oracle.jdbc
+ orai18n
+
@@ -53,6 +61,18 @@
logback-classic
1.2.3
+
+ com.oracle.jdbc
+ ojdbc8
+ 12.2.0.1
+ compile
+
+
+ com.oracle.jdbc
+ orai18n
+ 12.2.0.1
+ compile
+
@@ -139,8 +159,26 @@
true
+
+ maven.oracle.com
+
+ true
+
+
+ false
+
+ https://maven.oracle.com
+ default
+
+
+
+ maven.oracle.com
+ https://maven.oracle.com
+
+
+
utPLSQL-local
diff --git a/src/main/java/org/utplsql/cli/ReporterManager.java b/src/main/java/org/utplsql/cli/ReporterManager.java
index 3c33575..fe846a7 100644
--- a/src/main/java/org/utplsql/cli/ReporterManager.java
+++ b/src/main/java/org/utplsql/cli/ReporterManager.java
@@ -19,6 +19,8 @@
class ReporterManager {
private List reporterOptionsList;
+ private List reporterGatherErrors;
+ private ExecutorService executorService;
ReporterManager(List reporterParams ) {
initReporterOptionsList(reporterParams);
@@ -49,6 +51,25 @@ private void initReporterOptionsList( List reporterParams ) {
}
}
+ private void abortGathering(Throwable e) {
+ addGatherError(e);
+ executorService.shutdownNow();
+ }
+
+ private void addGatherError( Throwable e ) {
+ if ( reporterGatherErrors == null ) {
+ reporterGatherErrors = new ArrayList<>();
+ }
+ reporterGatherErrors.add(e);
+ }
+
+ boolean haveGatherErrorsOccured() {
+ return reporterGatherErrors != null && !reporterGatherErrors.isEmpty();
+ }
+
+ List getGatherErrors() {
+ return reporterGatherErrors;
+ }
/** Initializes the reporters so we can use the id to gather results
*
@@ -56,7 +77,7 @@ private void initReporterOptionsList( List reporterParams ) {
* @return List of Reporters
* @throws SQLException
*/
- public List initReporters(Connection conn, ReporterFactory reporterFactory, CompatibilityProxy compatibilityProxy) throws SQLException
+ List initReporters(Connection conn, ReporterFactory reporterFactory, CompatibilityProxy compatibilityProxy) throws SQLException
{
final List reporterList = new ArrayList<>();
@@ -79,10 +100,14 @@ public List initReporters(Connection conn, ReporterFactory reporterFac
*
* @param executorService
* @param dataSource
- * @param returnCode
*/
- public void startReporterGatherers(ExecutorService executorService, final DataSource dataSource, final int[] returnCode)
+ void startReporterGatherers(ExecutorService executorService, final DataSource dataSource)
{
+ if ( this.executorService != null && !this.executorService.isShutdown())
+ throw new IllegalStateException("There is already a running executor service!");
+
+ this.executorService = executorService;
+
// TODO: Implement Init-check
// Gather each reporter results on a separate thread.
for (ReporterOptions ro : reporterOptionsList) {
@@ -103,9 +128,7 @@ public void startReporterGatherers(ExecutorService executorService, final DataSo
ro.getReporterObj().getOutputBuffer().printAvailable(conn, printStreams);
} catch (SQLException | FileNotFoundException e) {
- System.out.println(e.getMessage());
- returnCode[0] = Cli.DEFAULT_ERROR_CODE;
- executorService.shutdownNow();
+ abortGathering(e);
} finally {
if (fileOutStream != null)
fileOutStream.close();
@@ -114,9 +137,9 @@ public void startReporterGatherers(ExecutorService executorService, final DataSo
}
}
- public List getReporterOptionsList() {
+ List getReporterOptionsList() {
return reporterOptionsList;
}
- public int getNumberOfReporters() { return reporterOptionsList.size(); }
+ int getNumberOfReporters() { return reporterOptionsList.size(); }
}
diff --git a/src/main/java/org/utplsql/cli/RunCommand.java b/src/main/java/org/utplsql/cli/RunCommand.java
index 13d461a..cbfca36 100644
--- a/src/main/java/org/utplsql/cli/RunCommand.java
+++ b/src/main/java/org/utplsql/cli/RunCommand.java
@@ -2,17 +2,20 @@
import com.beust.jcommander.Parameter;
import com.beust.jcommander.Parameters;
+import com.zaxxer.hikari.HikariDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.utplsql.api.*;
import org.utplsql.api.compatibility.CompatibilityProxy;
import org.utplsql.api.db.DefaultDatabaseInformation;
import org.utplsql.api.exception.DatabaseNotCompatibleException;
+import org.utplsql.api.exception.OracleCreateStatmenetStuckException;
import org.utplsql.api.exception.SomeTestsFailedException;
import org.utplsql.api.exception.UtPLSQLNotInstalledException;
import org.utplsql.api.reporter.Reporter;
import org.utplsql.api.reporter.ReporterFactory;
import org.utplsql.cli.exception.DatabaseConnectionFailed;
+import org.utplsql.cli.exception.ReporterTimeoutException;
import org.utplsql.cli.log.StringBlockFormatter;
import javax.sql.DataSource;
@@ -22,12 +25,12 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.TimeUnit;
+import java.util.concurrent.*;
/**
- * Created by vinicius.moreira on 19/04/2017.
+ * Issues a Run-Command with all the options
+ *
+ * Uses an executor to start a RunTestRunnerTask and one ReporterGatheringTask per Reporter requested.
*
* @author vinicious moreira
* @author pesse
@@ -138,27 +141,17 @@ else if ( logDebug ) {
LoggerConfiguration.configure(level);
}
- public int run() {
+ public int doRun() throws OracleCreateStatmenetStuckException {
init();
outputMainInformation();
+ HikariDataSource dataSource = null;
+ int returnCode = 0;
try {
final List reporterList;
- final File baseDir = new File("").getAbsoluteFile();
- final FileMapperOptions[] sourceMappingOptions = {null};
- final FileMapperOptions[] testMappingOptions = {null};
-
- final int[] returnCode = {0};
-
- sourceMappingOptions[0] = getFileMapperOptionsByParamListItem(this.sourcePathParams, baseDir);
- testMappingOptions[0] = getFileMapperOptionsByParamListItem(this.testPathParams, baseDir);
-
- final List finalIncludeObjectsList = getObjectList(includeObjects);
- final List finalExcludeObjectsList = getObjectList(excludeObjects);
-
- final DataSource dataSource = DataSourceProvider.getDataSource(getConnectionInfo(), getReporterManager().getNumberOfReporters() + 1);
+ dataSource = (HikariDataSource) DataSourceProvider.getDataSource(getConnectionInfo(), getReporterManager().getNumberOfReporters() + 2);
initDatabase(dataSource);
reporterList = initReporters(dataSource);
@@ -172,48 +165,77 @@ public int run() {
ExecutorService executorService = Executors.newFixedThreadPool(1 + reporterList.size());
// Run tests.
- executorService.submit(() -> {
- try (Connection conn = dataSource.getConnection()) {
- TestRunner testRunner = new TestRunner()
- .addPathList(testPaths)
- .addReporterList(reporterList)
- .sourceMappingOptions(sourceMappingOptions[0])
- .testMappingOptions(testMappingOptions[0])
- .colorConsole(this.colorConsole)
- .failOnErrors(true)
- .skipCompatibilityCheck(skipCompatibilityCheck)
- .includeObjects(finalIncludeObjectsList)
- .excludeObjects(finalExcludeObjectsList);
-
- logger.info("Running tests now.");
- logger.info("--------------------------------------");
- testRunner.run(conn);
- } catch (SomeTestsFailedException e) {
- returnCode[0] = this.failureExitCode;
- } catch (SQLException e) {
- System.out.println(e.getMessage());
- returnCode[0] = Cli.DEFAULT_ERROR_CODE;
- executorService.shutdownNow();
- }
- });
+ Future future = executorService.submit(new RunTestRunnerTask(dataSource, newTestRunner(reporterList)));
// Gather each reporter results on a separate thread.
- getReporterManager().startReporterGatherers(executorService, dataSource, returnCode);
-
- executorService.shutdown();
- executorService.awaitTermination(timeoutInMinutes, TimeUnit.MINUTES);
+ getReporterManager().startReporterGatherers(executorService, dataSource);
+
+ try {
+ future.get(timeoutInMinutes, TimeUnit.MINUTES);
+ } catch (TimeoutException e) {
+ executorService.shutdownNow();
+ throw new ReporterTimeoutException(timeoutInMinutes);
+ } catch (ExecutionException e) {
+ if (e.getCause() instanceof SomeTestsFailedException) {
+ returnCode = failureExitCode;
+ } else {
+ executorService.shutdownNow();
+ throw e.getCause();
+ }
+ } catch (InterruptedException e) {
+ executorService.shutdownNow();
+ throw e;
+ }
+ finally {
+ executorService.shutdown();
+ if (!executorService.awaitTermination(timeoutInMinutes, TimeUnit.MINUTES)) {
+ throw new ReporterTimeoutException(timeoutInMinutes);
+ }
+ }
logger.info("--------------------------------------");
logger.info("All tests done.");
-
- return returnCode[0];
- }
- catch ( DatabaseNotCompatibleException | UtPLSQLNotInstalledException | DatabaseConnectionFailed e ) {
+ } catch ( OracleCreateStatmenetStuckException e ) {
+ throw e;
+ } catch ( DatabaseNotCompatibleException | UtPLSQLNotInstalledException | DatabaseConnectionFailed | ReporterTimeoutException e ) {
System.out.println(e.getMessage());
- } catch (Exception e) {
+ returnCode = Cli.DEFAULT_ERROR_CODE;
+ } catch (Throwable e) {
e.printStackTrace();
+ returnCode = Cli.DEFAULT_ERROR_CODE;
+ } finally {
+ if ( dataSource != null )
+ dataSource.close();
}
- return 1;
+ return returnCode;
+ }
+
+ public int run() {
+ for ( int i = 1; i<5; i++ ) {
+ try {
+ return doRun();
+ } catch (OracleCreateStatmenetStuckException e) {
+ logger.warn("WARNING: Caught Oracle stuck during creation of Runner-Statement. Retrying ({})", i);
+ }
+ }
+
+ return Cli.DEFAULT_ERROR_CODE;
+ }
+
+ private TestRunner newTestRunner( List reporterList) {
+
+ final File baseDir = new File("").getAbsoluteFile();
+
+ return new TestRunner()
+ .addPathList(testPaths)
+ .addReporterList(reporterList)
+ .sourceMappingOptions(getFileMapperOptionsByParamListItem(this.sourcePathParams, baseDir))
+ .testMappingOptions(getFileMapperOptionsByParamListItem(this.testPathParams, baseDir))
+ .colorConsole(this.colorConsole)
+ .failOnErrors(true)
+ .skipCompatibilityCheck(skipCompatibilityCheck)
+ .includeObjects(getObjectList(includeObjects))
+ .excludeObjects(getObjectList(excludeObjects));
}
private ArrayList getObjectList(String includeObjects) {
diff --git a/src/main/java/org/utplsql/cli/RunTestRunnerTask.java b/src/main/java/org/utplsql/cli/RunTestRunnerTask.java
new file mode 100644
index 0000000..99c388f
--- /dev/null
+++ b/src/main/java/org/utplsql/cli/RunTestRunnerTask.java
@@ -0,0 +1,65 @@
+package org.utplsql.cli;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.utplsql.api.TestRunner;
+import org.utplsql.api.exception.OracleCreateStatmenetStuckException;
+import org.utplsql.api.exception.SomeTestsFailedException;
+
+import javax.sql.DataSource;
+import java.sql.Connection;
+import java.sql.SQLException;
+import java.util.concurrent.Callable;
+import java.util.concurrent.Executors;
+
+/** Runs the utPLSQL Test-Runner
+ *
+ * Takes care of its connection.
+ * In case of an OracleCreateStatementStuckException it will abort the connection, otherwise close it.
+ *
+ * @author pesse
+ */
+public class RunTestRunnerTask implements Callable {
+
+ private static final Logger logger = LoggerFactory.getLogger(RunTestRunnerTask.class);
+ private DataSource dataSource;
+ private TestRunner testRunner;
+
+ RunTestRunnerTask(DataSource dataSource, TestRunner testRunner) {
+ this.dataSource = dataSource;
+ this.testRunner = testRunner;
+ }
+
+ @Override
+ public Boolean call() throws Exception {
+ Connection conn = null;
+ try {
+ conn = dataSource.getConnection();
+ logger.info("Running tests now.");
+ logger.info("--------------------------------------");
+ testRunner.run(conn);
+ } catch (SomeTestsFailedException e) {
+ throw e;
+ } catch (OracleCreateStatmenetStuckException e ) {
+ try {
+ conn.abort(Executors.newSingleThreadExecutor());
+ conn = null;
+ } catch (SQLException e1) {
+ logger.error(e1.getMessage(), e1);
+ }
+ throw e;
+ } catch (SQLException e) {
+ System.out.println(e.getMessage());
+ throw e;
+ } finally {
+ if ( conn != null ) {
+ try {
+ conn.close();
+ } catch (SQLException e) {
+ logger.error(e.getMessage(), e);
+ }
+ }
+ }
+ return true;
+ }
+}
diff --git a/src/main/java/org/utplsql/cli/TestRunnerTask.java b/src/main/java/org/utplsql/cli/TestRunnerTask.java
new file mode 100644
index 0000000..4b18f1f
--- /dev/null
+++ b/src/main/java/org/utplsql/cli/TestRunnerTask.java
@@ -0,0 +1,24 @@
+package org.utplsql.cli;
+
+import org.utplsql.api.TestRunner;
+
+import java.sql.Connection;
+import java.util.concurrent.ExecutorService;
+
+class TestRunnerTask implements Runnable {
+
+ private Connection con;
+ private TestRunner testRunner;
+ private ExecutorService executorService;
+
+ TestRunnerTask(Connection con, TestRunner testRunner, ExecutorService executorService ) {
+ this.con = con;
+ this.testRunner = testRunner;
+ this.executorService = executorService;
+ }
+
+ @Override
+ public void run() {
+
+ }
+}
diff --git a/src/main/java/org/utplsql/cli/exception/ReporterTimeoutException.java b/src/main/java/org/utplsql/cli/exception/ReporterTimeoutException.java
new file mode 100644
index 0000000..39ce2b1
--- /dev/null
+++ b/src/main/java/org/utplsql/cli/exception/ReporterTimeoutException.java
@@ -0,0 +1,15 @@
+package org.utplsql.cli.exception;
+
+public class ReporterTimeoutException extends Exception {
+
+ private final int timeOutInMinutes;
+
+ public ReporterTimeoutException( int timeoutInMinutes ) {
+ super("Timeout while waiting for reporters to finish for " + timeoutInMinutes + " minutes");
+ this.timeOutInMinutes = timeoutInMinutes;
+ }
+
+ public int getTimeOutInMinutes() {
+ return timeOutInMinutes;
+ }
+}
diff --git a/src/test/java/org/utplsql/cli/RunCommandIssue20Test.java b/src/test/java/org/utplsql/cli/RunCommandIssue20Test.java
new file mode 100644
index 0000000..6b847f8
--- /dev/null
+++ b/src/test/java/org/utplsql/cli/RunCommandIssue20Test.java
@@ -0,0 +1,48 @@
+package org.utplsql.cli;
+
+import java.util.List;
+
+import org.junit.jupiter.api.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.utplsql.api.reporter.CoreReporters;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+/**
+ * Unit test for run command.
+ *
+ * @author philipp salivsberg
+ */
+class RunCommandIssue20Test {
+
+ private static final Logger logger = LoggerFactory.getLogger(RunCommandIssue20Test.class);
+
+ @Test
+ void runLoop() {
+ RunCommand runCmd = TestHelper.createRunCommand(
+ TestHelper.getConnectionString(),
+ "-p=TEST_BETWNSTR.normal_case",
+ "-f=ut_documentation_reporter");
+ List reporterOptionsList = runCmd.getReporterOptionsList();
+ ReporterOptions reporterOptions1 = reporterOptionsList.get(0);
+ assertEquals(CoreReporters.UT_DOCUMENTATION_REPORTER.name(), reporterOptions1.getReporterName());
+ assertTrue(reporterOptions1.outputToScreen());
+ // Loop in same JVM, uses a lot of new connections without closing existing ones, this might lead to
+ // "Could not establish connection to database. Reason: IO Error: Got minus one from a read call"
+ // before hitting the hanger at oracle.jdbc.driver.OracleStruct.getOracleAttributes(OracleStruct.java:347)
+ // You may increase processes and implicitly sessions by "alter system set processes=1024 scope=spfile;"
+ for (int i=0; i <= 120; i++) {
+ logger.info("=======================");
+ logger.info("Loop number " + i);
+ logger.info("=======================");
+ int result = runCmd.run();
+ if (result != 0) {
+ logger.error("Got an error during run. Return Code was " + result + "." );
+ break;
+ }
+ }
+ }
+
+}
\ No newline at end of file