├── assets └── demo_project │ └── source │ ├── script.sql │ ├── packages │ ├── package.pkb │ └── package.pks │ └── triggers │ └── trigger.trg ├── src ├── main │ ├── resources │ │ └── utplsql-cli.version │ ├── java │ │ └── org │ │ │ └── utplsql │ │ │ └── cli │ │ │ ├── exception │ │ │ ├── DatabaseConnectionFailed.java │ │ │ └── ReporterTimeoutException.java │ │ │ ├── reporters │ │ │ ├── ReporterOptionsAware.java │ │ │ └── LocalAssetsCoverageHTMLReporter.java │ │ │ ├── IRunCommand.java │ │ │ ├── ICommand.java │ │ │ ├── config │ │ │ ├── ConnectionConfig.java │ │ │ ├── ReporterConfig.java │ │ │ ├── FileMapperConfig.java │ │ │ └── RunCommandConfig.java │ │ │ ├── UtplsqlPicocliCommand.java │ │ │ ├── datasource │ │ │ ├── InitializableOracleDataSource.java │ │ │ └── TestedDataSourceProvider.java │ │ │ ├── OracleLibraryChecker.java │ │ │ ├── ReporterFactoryProvider.java │ │ │ ├── CliVersionInfo.java │ │ │ ├── FileWalker.java │ │ │ ├── ConnectionConfig.java │ │ │ ├── DataSourceProvider.java │ │ │ ├── ReporterOptions.java │ │ │ ├── VersionInfoCommand.java │ │ │ ├── Cli.java │ │ │ ├── LoggerConfiguration.java │ │ │ ├── RunTestRunnerTask.java │ │ │ ├── log │ │ │ └── StringBlockFormatter.java │ │ │ ├── LocaleInitializer.java │ │ │ ├── ReportersCommand.java │ │ │ ├── ReporterManager.java │ │ │ ├── RunPicocliCommand.java │ │ │ └── RunAction.java │ └── assembly │ │ └── zip.xml └── test │ ├── java │ └── org │ │ └── utplsql │ │ └── cli │ │ ├── CliVersionInfoTest.java │ │ ├── ReportersCommandIT.java │ │ ├── PathMapperTest.java │ │ ├── AbstractFileOutputTest.java │ │ ├── RunCommandConfigLevelTest.java │ │ ├── HelpCommandTest.java │ │ ├── RunCommandConfigParamsArePassedToTestRunnerTest.java │ │ ├── FileWalkerTest.java │ │ ├── VersionInfoCommandIT.java │ │ ├── StringBlockFormatterTest.java │ │ ├── CliHelpTest.java │ │ ├── ConnectionConfigTest.java │ │ ├── RunCommandIssue20IT.java │ │ ├── TestHelper.java │ │ ├── util │ │ └── SystemCapturer.java │ │ ├── DataSourceProviderIT.java │ │ ├── RunCommandArgumentsTest.java │ │ ├── RunCommandIT.java │ │ ├── RunCommandCoverageReporterIT.java │ │ ├── RunCommandTest.java │ │ └── PicocliRunCommandTest.java │ └── resources │ └── plsql │ ├── source │ └── betwnstr.sql │ └── test │ └── test_betwnstr.pkg ├── .idea ├── codeStyles │ ├── codeStyleConfig.xml │ └── Project.xml └── dictionaries │ └── utPLSQL.xml ├── .travis ├── create_api_user.sh ├── start_db.sh ├── create_release.sh ├── install_demo_project.sh └── install_utplsql.sh ├── .gitignore ├── .github └── workflows │ ├── release.yml │ └── build.yml ├── .travis.yml ├── pom.xml ├── LICENSE └── README.md /assets/demo_project/source/script.sql: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/demo_project/source/packages/package.pkb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/demo_project/source/packages/package.pks: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /assets/demo_project/source/triggers/trigger.trg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/main/resources/utplsql-cli.version: -------------------------------------------------------------------------------- 1 | ${project.version}.${travisBuildNumber} -------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/dictionaries/utPLSQL.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | utplsql 5 | 6 | 7 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/CliVersionInfoTest.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.assertTrue; 6 | 7 | class CliVersionInfoTest { 8 | 9 | @Test 10 | void getCliVersionInfo() { 11 | assertTrue(CliVersionInfo.getVersion().startsWith("3.1")); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/test/resources/plsql/source/betwnstr.sql: -------------------------------------------------------------------------------- 1 | create or replace function betwnstr( a_string varchar2, a_start_pos integer, a_end_pos integer ) return varchar2 is 2 | l_start_pos pls_integer := a_start_pos; 3 | begin 4 | if l_start_pos = 0 then 5 | l_start_pos := 1; 6 | end if; 7 | return substr( a_string, l_start_pos, a_end_pos - l_start_pos + 1); 8 | end; 9 | / 10 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/exception/DatabaseConnectionFailed.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli.exception; 2 | 3 | import java.sql.SQLException; 4 | 5 | public class DatabaseConnectionFailed extends SQLException { 6 | 7 | public DatabaseConnectionFailed(Throwable cause) { 8 | super("Could not establish connection to database. Reason: " + cause.getMessage(), cause); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/reporters/ReporterOptionsAware.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli.reporters; 2 | 3 | import org.utplsql.cli.ReporterOptions; 4 | 5 | /** 6 | * Reporters implementing this interface will get their specific ReporterOptions before initialization 7 | * 8 | * @author pesse 9 | */ 10 | public interface ReporterOptionsAware { 11 | void setReporterOptions(ReporterOptions options); 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/IRunCommand.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.utplsql.api.TestRunner; 4 | import org.utplsql.api.reporter.Reporter; 5 | 6 | import java.util.List; 7 | 8 | public interface IRunCommand extends ICommand { 9 | 10 | void initLogger(); 11 | 12 | TestRunner newTestRunner(List reporterList); 13 | 14 | List getReporterOptionsList(); 15 | } 16 | -------------------------------------------------------------------------------- /.travis/create_api_user.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ev 3 | 4 | sqlplus -S -L sys/oracle@//127.0.0.1:1521/xe AS SYSDBA < 2 | 3 | 4 | 9 | 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IntelliJ 2 | *.iml 3 | .idea/ 4 | 5 | # Maven 6 | target/ 7 | pom.xml.tag 8 | pom.xml.releaseBackup 9 | pom.xml.versionsBackup 10 | pom.xml.next 11 | release.properties 12 | dependency-reduced-pom.xml 13 | buildNumber.properties 14 | .mvn/timing.properties 15 | 16 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored) 17 | !/.mvn/wrapper/maven-wrapper.jar 18 | 19 | # Project exclude paths 20 | /.gradle/ 21 | /build/ 22 | /out/ -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/ICommand.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | /** 4 | * This is the very basic interface that should be implemented by all utPLSQL cli commands 5 | * 6 | * @author pesse 7 | */ 8 | public interface ICommand { 9 | 10 | /** 11 | * We expect the command to handle all eventually occurring exceptions 12 | * and return an exit code 13 | * 14 | * @return exit code integer 15 | */ 16 | int run(); 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/config/ConnectionConfig.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli.config; 2 | 3 | import java.beans.ConstructorProperties; 4 | 5 | public class ConnectionConfig { 6 | 7 | private final String connectString; 8 | 9 | @ConstructorProperties({"connectString"}) 10 | public ConnectionConfig(String connectString) { 11 | this.connectString = connectString; 12 | } 13 | 14 | public String getConnectString() { 15 | return connectString; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/exception/ReporterTimeoutException.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli.exception; 2 | 3 | public class ReporterTimeoutException extends Exception { 4 | 5 | private final int timeOutInMinutes; 6 | 7 | public ReporterTimeoutException(int timeoutInMinutes) { 8 | super("Timeout while waiting for reporters to finish for " + timeoutInMinutes + " minutes"); 9 | this.timeOutInMinutes = timeoutInMinutes; 10 | } 11 | 12 | public int getTimeOutInMinutes() { 13 | return timeOutInMinutes; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /.travis/start_db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ev 3 | 4 | # If docker credentials are not cached, do the login. 5 | if [ ! -f $DOCKER_CFG/config.json ]; then 6 | docker login -u "$DOCKER_USER" -p "$DOCKER_PASSWORD" 7 | else 8 | echo "Using docker login from cache..." 9 | fi 10 | 11 | # Pull the specified db version from docker hub. 12 | docker pull $DOCKER_REPO:$ORACLE_VERSION 13 | docker run -d --name $ORACLE_VERSION $DOCKER_OPTIONS -p 1521:1521 $DOCKER_REPO:$ORACLE_VERSION 14 | docker logs -f $ORACLE_VERSION | grep -m 1 "DATABASE IS READY TO USE!" --line-buffered 15 | -------------------------------------------------------------------------------- /src/main/assembly/zip.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | zip 6 | 7 | false 8 | 9 | 10 | zip 11 | 12 | 13 | 14 | 15 | ${project.build.directory}/appassembler 16 | /utPLSQL-cli 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/UtplsqlPicocliCommand.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import picocli.CommandLine; 4 | 5 | @CommandLine.Command( 6 | name = "utplsql", 7 | description = "utPLSQL cli", 8 | subcommands = { 9 | RunPicocliCommand.class, 10 | VersionInfoCommand.class, 11 | ReportersCommand.class, 12 | CommandLine.HelpCommand.class 13 | }) 14 | public class UtplsqlPicocliCommand { 15 | 16 | public static final String COMMANDLINE_PARAM_DESCRIPTION = "/@//[:]/ OR /@ OR /@::"; 17 | 18 | @CommandLine.Option(names = "-h", usageHelp = true, description = "display this help and exit") 19 | boolean help; 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/PathMapperTest.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.utplsql.api.TestRunner; 5 | 6 | import java.util.ArrayList; 7 | 8 | public class PathMapperTest { 9 | 10 | @Test 11 | void checkPathMapperOutput() { 12 | IRunCommand runCmd = TestHelper.createRunCommand(TestHelper.getConnectionString(), 13 | "-f=ut_sonar_test_reporter", 14 | "-o=sonar_result.xml", 15 | "-s", 16 | "-d", 17 | "-source_path=src/test/resources/plsql/source", 18 | "-owner=app", 19 | "-regex_expression=\"*\"", 20 | "-type_mapping=\"sql=PACKAGE BODY\"", 21 | "-test_path=src/test/resources/plsql/test" 22 | ); 23 | 24 | TestRunner testRunner = runCmd.newTestRunner(new ArrayList<>());; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/datasource/InitializableOracleDataSource.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli.datasource; 2 | 3 | import oracle.jdbc.pool.OracleDataSource; 4 | 5 | import java.sql.CallableStatement; 6 | import java.sql.Connection; 7 | import java.sql.SQLException; 8 | 9 | public class InitializableOracleDataSource extends OracleDataSource { 10 | 11 | private String initSql; 12 | 13 | public InitializableOracleDataSource() throws SQLException { 14 | } 15 | 16 | @Override 17 | public Connection getConnection() throws SQLException { 18 | Connection con = super.getConnection(); 19 | 20 | if ( initSql != null && !initSql.isEmpty() ) { 21 | try (CallableStatement stmt = con.prepareCall(initSql)) { 22 | stmt.execute(); 23 | } 24 | } 25 | 26 | return con; 27 | } 28 | 29 | public void setConnectionInitSql( String sql ) { 30 | this.initSql = sql; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/config/ReporterConfig.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli.config; 2 | 3 | import org.utplsql.api.reporter.CoreReporters; 4 | 5 | import java.beans.ConstructorProperties; 6 | 7 | public class ReporterConfig { 8 | 9 | private final String name; 10 | private final String output; 11 | private boolean forceToScreen = false; 12 | 13 | @ConstructorProperties({"name", "output", "forceToScreen"}) 14 | public ReporterConfig(String name, String output, Boolean forceToScreen) { 15 | if ( name != null ) { 16 | this.name = name; 17 | } else { 18 | this.name = CoreReporters.UT_DOCUMENTATION_REPORTER.name(); 19 | } 20 | this.output = output; 21 | if (forceToScreen != null) this.forceToScreen = forceToScreen; 22 | } 23 | 24 | public String getName() { 25 | return name; 26 | } 27 | 28 | public String getOutput() { 29 | return output; 30 | } 31 | 32 | public boolean isForceToScreen() { 33 | return forceToScreen; 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/OracleLibraryChecker.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | /** 4 | * Simple class to check whether needed Oracle libraries are on classpath or not 5 | * 6 | * @author pesse 7 | */ 8 | class OracleLibraryChecker { 9 | 10 | private static boolean classExists(String classFullName) { 11 | try { 12 | Class.forName(classFullName); 13 | 14 | return true; 15 | } catch (ClassNotFoundException e) { 16 | return false; 17 | } 18 | } 19 | 20 | /** 21 | * Checks if OJDBC library is on the classpath by searching for oracle.jdbc.OracleDriver class 22 | * 23 | * @return true or false 24 | */ 25 | public static boolean checkOjdbcExists() { 26 | return classExists("oracle.jdbc.OracleDriver"); 27 | } 28 | 29 | /** 30 | * Checks if Orai18n library is on the classpath by searching for oracle.i18n.text.OraCharset 31 | * 32 | * @return true or false 33 | */ 34 | public static boolean checkOrai18nExists() { 35 | return classExists("oracle.i18n.text.OraCharset"); 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /.travis/create_release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -ev 3 | 4 | VERSION=`date +%Y%m%d%H%M` 5 | MVN_VERSION=$(mvn help:evaluate -Dexpression=project.version -q -DforceStdout) 6 | # Strip -SNAPSHOT if exists 7 | MVN_VERSION=${MVN_VERSION/-SNAPSHOT/} 8 | 9 | #overcome maven assemble issue: https://github.com/mojohaus/appassembler/issues/61 10 | sed -i '/CYGWIN\*) cygwin=true/c\ CYGWIN*|MINGW*) cygwin=true ;;' target/appassembler/bin/utplsql 11 | 12 | mkdir dist 13 | mv target/appassembler utPLSQL-cli 14 | 15 | zip -r -q dist/utPLSQL-cli-${TRAVIS_BRANCH}-${VERSION}.zip utPLSQL-cli 16 | zip -r -q utPLSQL-cli.zip utPLSQL-cli 17 | md5sum utPLSQL-cli.zip --tag > utPLSQL-cli.zip.md5 18 | 19 | cat > bintray.json < tempPaths; 14 | 15 | protected void addTempPath(Path path) { 16 | tempPaths.add(path); 17 | } 18 | 19 | protected boolean tempPathExists( Path path ) { return tempPaths.contains(path); } 20 | 21 | @BeforeEach 22 | public void setupTest() { 23 | tempPaths = new HashSet<>(); 24 | } 25 | 26 | @AfterEach 27 | public void deleteTempFiles() { 28 | tempPaths.forEach(p -> deleteDir(p.toFile())); 29 | } 30 | 31 | void deleteDir(File file) { 32 | if (file.exists()) { 33 | File[] contents = file.listFiles(); 34 | if (contents != null) { 35 | for (File f : contents) { 36 | deleteDir(f); 37 | } 38 | } 39 | file.delete(); 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /.travis/install_demo_project.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -evx 3 | cd $(dirname $(readlink -f $0)) 4 | 5 | PROJECT_FILE="utPLSQL-demo-project" 6 | git clone -b develop --single-branch https://github.com/utPLSQL/utPLSQL-demo-project.git 7 | 8 | cat > demo_project.sh.tmp < install.sh.tmp < getFileList(File baseDir, String inspectPath) { 13 | return getFileList(baseDir, inspectPath, true); 14 | } 15 | 16 | public List getFileList(File baseDir, String inspectPath, boolean relative) { 17 | File inspectDir = new File(baseDir, inspectPath); 18 | 19 | if (!inspectDir.isDirectory()) { 20 | throw new IllegalArgumentException(inspectPath + " is not a directory."); 21 | } 22 | 23 | List fileList = new ArrayList<>(); 24 | listDirFiles(baseDir, inspectDir, fileList, relative); 25 | 26 | return fileList; 27 | } 28 | 29 | private void listDirFiles(File baseDir, File directory, List fileList, boolean relative) { 30 | File[] directoryFiles = directory.listFiles(); 31 | 32 | if (directoryFiles == null) { 33 | return; 34 | } 35 | 36 | for (File file : directoryFiles) { 37 | if (file.isFile()) { 38 | String absolutePath = file.getAbsolutePath(); 39 | 40 | if (relative) { 41 | absolutePath = absolutePath.substring(baseDir.getAbsolutePath().length() + 1); 42 | } 43 | 44 | fileList.add(absolutePath); 45 | } else { 46 | listDirFiles(baseDir, file, fileList, relative); 47 | } 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/ConnectionConfig.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | public class ConnectionConfig { 7 | 8 | private final String user; 9 | private final String password; 10 | private final String connect; 11 | 12 | public ConnectionConfig(String connectString) { 13 | Matcher m = Pattern.compile("^(\".+\"|[^/]+)/(\".+\"|[^@]+)@(.*)$").matcher(connectString); 14 | if (m.find()) { 15 | user = stripEnclosingQuotes(m.group(1)); 16 | password = stripEnclosingQuotes(m.group(2)); 17 | connect = m.group(3); 18 | } else { 19 | throw new IllegalArgumentException("Not a valid connectString: '" + connectString + "'"); 20 | } 21 | } 22 | 23 | private String stripEnclosingQuotes(String value) { 24 | if (value.length() > 1 25 | && value.startsWith("\"") 26 | && value.endsWith("\"")) { 27 | return value.substring(1, value.length() - 1); 28 | } else { 29 | return value; 30 | } 31 | } 32 | 33 | public String getConnect() { 34 | return connect; 35 | } 36 | 37 | public String getUser() { 38 | return user; 39 | } 40 | 41 | public String getPassword() { 42 | return password; 43 | } 44 | 45 | public String getConnectString() { 46 | return user + "/" + password + "@" + connect; 47 | } 48 | 49 | public boolean isSysDba() { 50 | return user != null && 51 | (user.toLowerCase().endsWith(" as sysdba") 52 | || user.toLowerCase().endsWith(" as sysoper")); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/RunCommandConfigParamsArePassedToTestRunnerTest.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.utplsql.api.TestRunner; 5 | import org.utplsql.cli.config.RunCommandConfig; 6 | 7 | import java.util.ArrayList; 8 | 9 | import static org.hamcrest.MatcherAssert.assertThat; 10 | import static org.hamcrest.Matchers.contains; 11 | import static org.hamcrest.Matchers.equalTo; 12 | import static org.junit.jupiter.api.Assertions.assertTrue; 13 | 14 | public class RunCommandConfigParamsArePassedToTestRunnerTest { 15 | 16 | @Test 17 | void tags() { 18 | RunCommandConfig config = new RunCommandConfig.Builder() 19 | .tags(new String[]{"tag1", "tag2"}) 20 | .create(); 21 | TestRunner testRunner = new RunAction(config).newTestRunner(new ArrayList<>()); 22 | assertThat( testRunner.getOptions().tags, contains("tag1", "tag2") ); 23 | } 24 | 25 | @Test 26 | void coverageSchemes() { 27 | RunCommandConfig config = new RunCommandConfig.Builder() 28 | .coverageSchemes(new String[]{"schema1", "another_schema", "and-another-one"}) 29 | .create(); 30 | TestRunner testRunner = new RunAction(config).newTestRunner(new ArrayList<>()); 31 | assertThat( testRunner.getOptions().coverageSchemes, contains("schema1", "another_schema", "and-another-one") ); 32 | } 33 | 34 | @Test 35 | void oraStuckTimeout() { 36 | RunCommandConfig config = new RunCommandConfig.Builder() 37 | .oraStuckTimeout(2) 38 | .create(); 39 | TestRunner testRunner = new RunAction(config).newTestRunner(new ArrayList<>()); 40 | assertThat( testRunner.getOptions().oraStuckTimeout, equalTo(2) ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/FileWalkerTest.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.io.File; 6 | import java.util.Collections; 7 | import java.util.List; 8 | 9 | import static org.junit.jupiter.api.Assertions.assertArrayEquals; 10 | 11 | /** 12 | * Created by Vinicius on 18/06/2017. 13 | */ 14 | class FileWalkerTest { 15 | 16 | private final File BASE_DIR = new File(new File("").getAbsolutePath(), "assets/demo_project"); 17 | 18 | @Test 19 | void fileWalker_Relative() { 20 | List fileList = new FileWalker().getFileList(BASE_DIR, "source"); 21 | Collections.sort(fileList); 22 | assertArrayEquals(new Object[] { 23 | "source/packages/package.pkb".replace('/', File.separatorChar), 24 | "source/packages/package.pks".replace('/', File.separatorChar), 25 | "source/script.sql".replace('/', File.separatorChar), 26 | "source/triggers/trigger.trg".replace('/', File.separatorChar), 27 | }, fileList.toArray()); 28 | } 29 | 30 | @Test 31 | void fileWalker_Absolute() { 32 | List fileList = new FileWalker().getFileList(BASE_DIR, "source", false); 33 | Collections.sort(fileList); 34 | assertArrayEquals(new Object[] { 35 | BASE_DIR.getAbsolutePath() + "/source/packages/package.pkb".replace('/', File.separatorChar), 36 | BASE_DIR.getAbsolutePath() + "/source/packages/package.pks".replace('/', File.separatorChar), 37 | BASE_DIR.getAbsolutePath() + "/source/script.sql".replace('/', File.separatorChar), 38 | BASE_DIR.getAbsolutePath() + "/source/triggers/trigger.trg".replace('/', File.separatorChar), 39 | }, fileList.toArray()); 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/VersionInfoCommandIT.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.AfterEach; 4 | import org.junit.jupiter.api.BeforeEach; 5 | import org.junit.jupiter.api.Test; 6 | import org.utplsql.cli.util.SystemCapturer; 7 | 8 | import java.util.Arrays; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | 12 | class VersionInfoCommandIT { 13 | 14 | private SystemCapturer capturer; 15 | 16 | @BeforeEach 17 | void setupCaptureSystemOut() { 18 | capturer = new SystemCapturer.SystemOutCapturer(); 19 | } 20 | 21 | private int getNonEmptyLines(String content) { 22 | return (int) Arrays.stream(content.split("[\n|\r]")) 23 | .filter(line -> !line.isEmpty()) 24 | .count(); 25 | } 26 | 27 | private void assertNumberOfLines( int expected, String content ) { 28 | int numOfLines = getNonEmptyLines(content); 29 | assertEquals(expected, numOfLines, String.format("Expected output to have %n lines, but got %n", expected, numOfLines)); 30 | } 31 | @Test 32 | void infoCommandRunsWithoutConnection() { 33 | 34 | capturer.start(); 35 | 36 | int result = TestHelper.runApp("info"); 37 | 38 | String output = capturer.stop(); 39 | 40 | assertEquals(0, result); 41 | assertNumberOfLines(2, output); 42 | } 43 | @Test 44 | void infoCommandRunsWithConnection() { 45 | 46 | capturer.start(); 47 | 48 | int result = TestHelper.runApp("info", TestHelper.getConnectionString()); 49 | 50 | String output = capturer.stop(); 51 | 52 | assertEquals(0, result); 53 | assertNumberOfLines(3, output); 54 | } 55 | 56 | @AfterEach 57 | void cleanupCaptureSystemOut() { 58 | capturer.stop(); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/config/FileMapperConfig.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli.config; 2 | 3 | import java.beans.ConstructorProperties; 4 | import java.util.Map; 5 | 6 | public class FileMapperConfig { 7 | 8 | private final String path; 9 | private final String owner; 10 | private final String regexExpression; 11 | private final Map typeMapping; 12 | private final Integer ownerSubexpression; 13 | private final Integer nameSubexpression; 14 | private final Integer typeSubexpression; 15 | 16 | @ConstructorProperties({"path", "owner", "regexExpression", "typeMapping", "ownerSubexpression", "nameSubexpression", "typeSubexpression"}) 17 | public FileMapperConfig(String path, String owner, String regexExpression, Map typeMapping, Integer ownerSubexpression, Integer nameSubexpression, Integer typeSubexpression) { 18 | this.path = path; 19 | this.owner = owner; 20 | this.regexExpression = regexExpression; 21 | this.typeMapping = typeMapping; 22 | this.ownerSubexpression = ownerSubexpression; 23 | this.nameSubexpression = nameSubexpression; 24 | this.typeSubexpression = typeSubexpression; 25 | } 26 | 27 | public String getPath() { 28 | return path; 29 | } 30 | 31 | public String getOwner() { 32 | return owner; 33 | } 34 | 35 | public String getRegexExpression() { 36 | return regexExpression; 37 | } 38 | 39 | public Map getTypeMapping() { 40 | return typeMapping; 41 | } 42 | 43 | public Integer getOwnerSubexpression() { 44 | return ownerSubexpression; 45 | } 46 | 47 | public Integer getNameSubexpression() { 48 | return nameSubexpression; 49 | } 50 | 51 | public Integer getTypeSubexpression() { 52 | return typeSubexpression; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/StringBlockFormatterTest.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.utplsql.cli.log.StringBlockFormatter; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | 8 | class StringBlockFormatterTest { 9 | 10 | @Test 11 | void getBlockFormattedString() { 12 | 13 | String expected = 14 | "#### Headline ####\n" + 15 | "# #\n" + 16 | "# My value 1 #\n" + 17 | "# My val 2 #\n" + 18 | "# #\n" + 19 | "##################"; 20 | 21 | StringBlockFormatter formatter = new StringBlockFormatter("Headline"); 22 | formatter.appendLine("My value 1"); 23 | formatter.appendLine("My val 2"); 24 | 25 | assertEquals( expected, formatter.toString() ); 26 | } 27 | 28 | @Test 29 | void getEncapsulatedLine() { 30 | 31 | String line = StringBlockFormatter.getEncapsulatedLine("val 1", 20); 32 | 33 | assertEquals("# val 1 #", line); 34 | } 35 | 36 | @Test 37 | void getEncapsulatedHeadline() { 38 | 39 | assertEquals("######### headline #########", 40 | StringBlockFormatter.getEncapsulatedHeadline("headline", 20)); 41 | assertEquals("######### headline ##########", 42 | StringBlockFormatter.getEncapsulatedHeadline("headline", 21)); 43 | assertEquals("######### headline1 #########", 44 | StringBlockFormatter.getEncapsulatedHeadline("headline1", 21)); 45 | assertEquals("######## headline1 #########", 46 | StringBlockFormatter.getEncapsulatedHeadline("headline1", 20)); 47 | } 48 | 49 | @Test 50 | void getEmptyEncapsulatedHeadline() { 51 | 52 | assertEquals("##################", 53 | StringBlockFormatter.getEncapsulatedHeadline("", 10)); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/DataSourceProvider.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.utplsql.cli.datasource.TestedDataSourceProvider; 4 | 5 | import javax.sql.DataSource; 6 | import java.io.File; 7 | import java.sql.SQLException; 8 | 9 | /** 10 | * Helper class to give you a ready-to-use datasource 11 | * 12 | * @author pesse 13 | */ 14 | public class DataSourceProvider { 15 | 16 | static { 17 | String oracleHome = System.getenv("ORACLE_HOME"); 18 | if (oracleHome != null && System.getProperty("oracle.net.tns_admin") == null) { 19 | System.setProperty("oracle.net.tns_admin", 20 | String.join(File.separator, oracleHome, "NETWORK", "ADMIN")); 21 | } 22 | } 23 | 24 | public static DataSource getDataSource(String connectString, int maxConnections) throws SQLException { 25 | 26 | requireOjdbc(); 27 | 28 | ConnectionConfig config = new ConnectionConfig(connectString); 29 | warnIfSysDba(config); 30 | 31 | return new TestedDataSourceProvider(config, maxConnections).getDataSource(); 32 | } 33 | 34 | private static void requireOjdbc() { 35 | if (!OracleLibraryChecker.checkOjdbcExists()) { 36 | System.out.println("Could not find Oracle JDBC driver in classpath. Please download the jar from Oracle website" + 37 | " and copy it to the 'lib' folder of your utPLSQL-cli installation."); 38 | System.out.println("Download from http://www.oracle.com/technetwork/database/features/jdbc/jdbc-ucp-122-3110062.html"); 39 | 40 | throw new RuntimeException("Can't run utPLSQL-cli without Oracle JDBC driver"); 41 | } 42 | } 43 | 44 | private static void warnIfSysDba(ConnectionConfig config) { 45 | if (config.isSysDba()) { 46 | System.out.println("WARNING: You are connecting to the database as SYSDBA or SYSOPER, which is NOT RECOMMENDED and can put your database at risk!"); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/ReporterOptions.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.utplsql.api.reporter.Reporter; 4 | 5 | /** 6 | * Created by Vinicius on 20/05/2017. 7 | */ 8 | public class ReporterOptions { 9 | 10 | private String reporterName; 11 | private String outputFileName; 12 | private boolean outputToScreen; 13 | private boolean forceOutputToScreen; 14 | 15 | private Reporter reporterObj = null; 16 | 17 | public ReporterOptions(String reporterName, String outputFileName) { 18 | setReporterName(reporterName); 19 | setOutputFileName(outputFileName); 20 | this.outputToScreen = (outputFileName == null); // If outputFileName is null we assume it should be sent to screen 21 | this.forceOutputToScreen = false; 22 | } 23 | 24 | public ReporterOptions(String reporterName) { 25 | this(reporterName, null); 26 | } 27 | 28 | public Reporter getReporterObj() { 29 | return reporterObj; 30 | } 31 | 32 | public void setReporterObj(Reporter reporterObj) { 33 | this.reporterObj = reporterObj; 34 | } 35 | 36 | public String getReporterName() { 37 | return reporterName.toUpperCase(); 38 | } 39 | 40 | public void setReporterName(String reporterName) { 41 | this.reporterName = reporterName; 42 | } 43 | 44 | public String getOutputFileName() { 45 | return outputFileName; 46 | } 47 | 48 | public void setOutputFileName(String outputFileName) { 49 | this.outputFileName = outputFileName; 50 | this.outputToScreen = false; 51 | } 52 | 53 | public boolean outputToFile() { 54 | return outputFileName != null && !outputFileName.isEmpty(); 55 | } 56 | 57 | public boolean outputToScreen() { 58 | return outputToScreen || forceOutputToScreen; 59 | } 60 | 61 | public void forceOutputToScreen(boolean outputToScreen) { 62 | this.forceOutputToScreen = outputToScreen; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/CliHelpTest.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.utplsql.cli.util.SystemCapturer; 5 | 6 | import static org.junit.jupiter.api.Assertions.assertEquals; 7 | import static org.junit.jupiter.api.Assertions.assertTrue; 8 | 9 | class CliHelpTest { 10 | 11 | private SystemCapturer capturer; 12 | 13 | @Test 14 | void show_basic_help_on_help_command() { 15 | capturer = new SystemCapturer.SystemOutCapturer(); 16 | capturer.start(); 17 | TestHelper.runApp("help"); 18 | String output = capturer.stop(); 19 | 20 | assertTrue(output.contains("Usage:")); 21 | } 22 | 23 | @Test 24 | void show_help_for_run_command() { 25 | capturer = new SystemCapturer.SystemOutCapturer(); 26 | capturer.start(); 27 | TestHelper.runApp("run", "-h"); 28 | String output = capturer.stop(); 29 | 30 | assertTrue(output.contains("Usage:")); 31 | } 32 | 33 | @Test 34 | void show_help_for_reporters_command() { 35 | capturer = new SystemCapturer.SystemOutCapturer(); 36 | capturer.start(); 37 | TestHelper.runApp("reporters", "-h"); 38 | String output = capturer.stop(); 39 | 40 | assertTrue(output.contains("Usage:")); 41 | } 42 | 43 | @Test 44 | void show_help_for_info_command() { 45 | capturer = new SystemCapturer.SystemOutCapturer(); 46 | capturer.start(); 47 | TestHelper.runApp("reporters", "-h"); 48 | String output = capturer.stop(); 49 | 50 | assertTrue(output.contains("Usage:")); 51 | } 52 | 53 | @Test 54 | void write_help_to_error_out_on_unknown_command() { 55 | capturer = new SystemCapturer.SystemErrCapturer(); 56 | capturer.start(); 57 | int exitCode = TestHelper.runApp("wtfhappens"); 58 | String output = capturer.stop(); 59 | 60 | assertTrue(output.contains("Usage:")); 61 | assertEquals(1, exitCode); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/VersionInfoCommand.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | 4 | import org.utplsql.api.JavaApiVersionInfo; 5 | import org.utplsql.api.Version; 6 | import org.utplsql.api.db.DefaultDatabaseInformation; 7 | import org.utplsql.api.exception.UtPLSQLNotInstalledException; 8 | import picocli.CommandLine.Command; 9 | import picocli.CommandLine.Option; 10 | import picocli.CommandLine.Parameters; 11 | 12 | import javax.sql.DataSource; 13 | import java.sql.Connection; 14 | import java.sql.SQLException; 15 | 16 | @Command(name = "info", description = "prints version information of cli, java-api and - if connection is given - database utPLSQL framework") 17 | public class VersionInfoCommand implements ICommand { 18 | 19 | @Parameters(description = UtplsqlPicocliCommand.COMMANDLINE_PARAM_DESCRIPTION, arity = "0..1") 20 | private String connectionString; 21 | 22 | @Option(names = "-h", usageHelp = true, description = "display this help and exit") 23 | boolean help; 24 | 25 | public int run() { 26 | LoggerConfiguration.configure(LoggerConfiguration.ConfigLevel.NONE); 27 | 28 | System.out.println(CliVersionInfo.getInfo()); 29 | System.out.println(JavaApiVersionInfo.getInfo()); 30 | 31 | try { 32 | writeUtPlsqlVersion(connectionString); 33 | } catch (SQLException e) { 34 | e.printStackTrace(); 35 | return 1; 36 | } 37 | 38 | return 0; 39 | } 40 | 41 | private void writeUtPlsqlVersion(String connectString) throws SQLException { 42 | if (connectString != null) { 43 | 44 | DataSource dataSource = DataSourceProvider.getDataSource(connectString, 1); 45 | 46 | try (Connection con = dataSource.getConnection()) { 47 | Version v = new DefaultDatabaseInformation().getUtPlsqlFrameworkVersion(con); 48 | System.out.println("utPLSQL " + v.getNormalizedString()); 49 | } catch (UtPLSQLNotInstalledException e) { 50 | System.out.println(e.getMessage()); 51 | } 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/ConnectionConfigTest.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import static org.junit.jupiter.api.Assertions.*; 6 | 7 | public class ConnectionConfigTest { 8 | 9 | @Test 10 | void parse() { 11 | ConnectionConfig info = new ConnectionConfig("test/pw@my.local.host/service"); 12 | 13 | assertEquals("test", info.getUser()); 14 | assertEquals("pw", info.getPassword()); 15 | assertEquals("my.local.host/service", info.getConnect()); 16 | assertFalse(info.isSysDba()); 17 | } 18 | 19 | @Test 20 | void parseSysDba() { 21 | ConnectionConfig info = new ConnectionConfig("sys as sysdba/pw@my.local.host/service"); 22 | 23 | assertEquals("sys as sysdba", info.getUser()); 24 | assertEquals("pw", info.getPassword()); 25 | assertEquals("my.local.host/service", info.getConnect()); 26 | assertTrue(info.isSysDba()); 27 | } 28 | @Test 29 | void parseSysOper() { 30 | ConnectionConfig info = new ConnectionConfig("myOperUser as sysoper/passw0rd@my.local.host/service"); 31 | 32 | assertEquals("myOperUser as sysoper", info.getUser()); 33 | assertEquals("passw0rd", info.getPassword()); 34 | assertEquals("my.local.host/service", info.getConnect()); 35 | assertTrue(info.isSysDba()); 36 | } 37 | 38 | @Test 39 | void parseSpecialCharsPW() { 40 | ConnectionConfig info = new ConnectionConfig("test/\"p@ssw0rd=\"@my.local.host/service"); 41 | 42 | assertEquals("test", info.getUser()); 43 | assertEquals("p@ssw0rd=", info.getPassword()); 44 | assertEquals("my.local.host/service", info.getConnect()); 45 | assertFalse(info.isSysDba()); 46 | } 47 | 48 | @Test 49 | void parseSpecialCharsUser() { 50 | ConnectionConfig info = new ConnectionConfig("\"User/Mine@=\"/pw@my.local.host/service"); 51 | 52 | assertEquals("User/Mine@=", info.getUser()); 53 | assertEquals("pw", info.getPassword()); 54 | assertEquals("my.local.host/service", info.getConnect()); 55 | assertFalse(info.isSysDba()); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/RunCommandIssue20IT.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.utplsql.api.reporter.CoreReporters; 7 | 8 | import java.util.List; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | import static org.junit.jupiter.api.Assertions.assertTrue; 12 | 13 | /** 14 | * Unit test for run command. 15 | * 16 | * @author philipp salivsberg 17 | */ 18 | class RunCommandIssue20IT { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(RunCommandIssue20IT.class); 21 | 22 | @Test 23 | void runLoop() { 24 | IRunCommand runCmd = TestHelper.createRunCommand( 25 | TestHelper.getConnectionString(), 26 | "-p=TEST_BETWNSTR.normal_case", 27 | "-f=ut_documentation_reporter", 28 | "--ora-stuck-timeout=3"); 29 | List reporterOptionsList = runCmd.getReporterOptionsList(); 30 | ReporterOptions reporterOptions1 = reporterOptionsList.get(0); 31 | assertEquals(CoreReporters.UT_DOCUMENTATION_REPORTER.name(), reporterOptions1.getReporterName()); 32 | assertTrue(reporterOptions1.outputToScreen()); 33 | // Loop in same JVM, uses a lot of new connections without closing existing ones, this might lead to 34 | // "Could not establish connection to database. Reason: IO Error: Got minus one from a read call" 35 | // before hitting the hanger at oracle.jdbc.driver.OracleStruct.getOracleAttributes(OracleStruct.java:347) 36 | // You may increase processes and implicitly sessions by "alter system set processes=1024 scope=spfile;" 37 | for (int i=0; i <= 120; i++) { 38 | logger.info("======================="); 39 | logger.info("Loop number " + i); 40 | logger.info("======================="); 41 | int result = runCmd.run(); 42 | if (result != 0) { 43 | logger.error("Got an error during run. Return Code was " + result + "." ); 44 | break; 45 | } 46 | } 47 | } 48 | 49 | } -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/Cli.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import picocli.CommandLine; 6 | 7 | import java.util.List; 8 | 9 | public class Cli { 10 | 11 | private static final Logger logger = LoggerFactory.getLogger(Cli.class); 12 | 13 | static final int DEFAULT_ERROR_CODE = 1; 14 | 15 | public static void main(String[] args) { 16 | 17 | int exitCode = runPicocliWithExitCode(args); 18 | 19 | System.exit(exitCode); 20 | } 21 | 22 | static int runPicocliWithExitCode(String[] args) { 23 | 24 | logger.debug("Args: "+String.join(", ", args)); 25 | 26 | CommandLine commandLine = new CommandLine(UtplsqlPicocliCommand.class); 27 | commandLine.setTrimQuotes(true); 28 | 29 | int exitCode = DEFAULT_ERROR_CODE; 30 | 31 | try { 32 | 33 | List parsedLines = commandLine.parse(args); 34 | 35 | boolean commandWasRun = false; 36 | for (CommandLine parsedLine : parsedLines) { 37 | if (parsedLine.isUsageHelpRequested()) { 38 | parsedLine.usage(System.out); 39 | return 0; 40 | } else if (parsedLine.isVersionHelpRequested()) { 41 | parsedLine.printVersionHelp(System.out); 42 | return 0; 43 | } 44 | 45 | Object command = parsedLine.getCommand(); 46 | if (command instanceof ICommand) { 47 | exitCode = ((ICommand) command).run(); 48 | commandWasRun = true; 49 | break; 50 | } 51 | } 52 | 53 | if (!commandWasRun) { 54 | commandLine.usage(System.out); 55 | } 56 | } catch (CommandLine.ParameterException e) { 57 | System.err.println(e.getMessage()); 58 | if (!CommandLine.UnmatchedArgumentException.printSuggestions(e, System.err)) { 59 | e.getCommandLine().usage(System.err); 60 | } 61 | } catch (Exception e) { 62 | e.printStackTrace(); 63 | } 64 | 65 | return exitCode; 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/LoggerConfiguration.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import ch.qos.logback.classic.Level; 4 | import ch.qos.logback.classic.Logger; 5 | import ch.qos.logback.classic.LoggerContext; 6 | import ch.qos.logback.classic.encoder.PatternLayoutEncoder; 7 | import ch.qos.logback.classic.spi.ILoggingEvent; 8 | import ch.qos.logback.core.ConsoleAppender; 9 | import org.slf4j.LoggerFactory; 10 | 11 | public class LoggerConfiguration { 12 | 13 | public enum ConfigLevel { 14 | BASIC, NONE, DEBUG 15 | } 16 | 17 | private LoggerConfiguration() { 18 | throw new UnsupportedOperationException(); 19 | } 20 | 21 | static void configure(ConfigLevel level) { 22 | switch (level) { 23 | case BASIC: 24 | configureInfo(); 25 | break; 26 | case NONE: 27 | configureSilent(); 28 | break; 29 | case DEBUG: 30 | configureDebug(); 31 | break; 32 | } 33 | } 34 | 35 | private static void configureSilent() { 36 | setRootLoggerLevel(Level.OFF); 37 | } 38 | 39 | private static void configureInfo() { 40 | setRootLoggerLevel(Level.INFO); 41 | setSingleConsoleAppenderWithLayout("%msg%n"); 42 | } 43 | 44 | private static void configureDebug() { 45 | setRootLoggerLevel(Level.DEBUG); 46 | setSingleConsoleAppenderWithLayout("%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"); 47 | } 48 | 49 | private static void setRootLoggerLevel(Level level) { 50 | Logger root = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); 51 | root.setLevel(level); 52 | } 53 | 54 | private static void setSingleConsoleAppenderWithLayout(String patternLayout) { 55 | Logger logger = (Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); 56 | LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory(); 57 | 58 | PatternLayoutEncoder ple = new PatternLayoutEncoder(); 59 | ple.setPattern(patternLayout); 60 | 61 | ple.setContext(lc); 62 | ple.start(); 63 | 64 | ConsoleAppender consoleAppender = new ConsoleAppender<>(); 65 | consoleAppender.setEncoder(ple); 66 | consoleAppender.setContext(lc); 67 | consoleAppender.start(); 68 | 69 | logger.detachAndStopAllAppenders(); 70 | logger.setAdditive(false); 71 | logger.addAppender(consoleAppender); 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: Build and test 2 | 3 | on: 4 | push: 5 | 6 | pull_request: 7 | branches: [ develop ] 8 | 9 | workflow_dispatch: 10 | 11 | repository_dispatch: 12 | types: [utPLSQL-build,utPLSQL-java-api-build] 13 | 14 | defaults: 15 | run: 16 | shell: bash 17 | 18 | jobs: 19 | build: 20 | name: Test on JDK ${{ matrix.jdk }} with utPLSQL ${{ matrix.utplsql_version }} 21 | runs-on: ubuntu-latest 22 | env: 23 | ORACLE_VERSION: "gvenzl/oracle-xe:18.4.0-slim" 24 | UTPLSQL_VERSION: ${{matrix.utplsql_version}} 25 | UTPLSQL_FILE: ${{matrix.utplsql_file}} 26 | ORACLE_PASSWORD: oracle 27 | DB_URL: "127.0.0.1:1521:XE" 28 | DB_USER: app 29 | DB_PASS: app 30 | 31 | strategy: 32 | fail-fast: false 33 | matrix: 34 | utplsql_version: ["v3.0.1","v3.0.2","v3.0.3","v3.0.4","v3.1.1","v3.1.2","v3.1.3","v3.1.6","v3.1.7","v3.1.8","v3.1.9","v3.1.10","v3.1.11","develop"] 35 | utplsql_file: ["utPLSQL"] 36 | jdk: ['8'] 37 | include: 38 | - utplsql_version: "v3.0.0" 39 | jdk: '8' 40 | utplsql_file: "utPLSQLv3.0.0" 41 | services: 42 | oracle: 43 | image: gvenzl/oracle-xe:18.4.0-slim 44 | env: 45 | ORACLE_PASSWORD: oracle 46 | ports: 47 | - 1521:1521 48 | options: >- 49 | --health-cmd healthcheck.sh 50 | --health-interval 10s 51 | --health-timeout 5s 52 | --health-retries 10 53 | --name oracle 54 | 55 | steps: 56 | - uses: actions/checkout@v2 57 | with: 58 | fetch-depth: 0 59 | - uses: actions/setup-java@v2 60 | with: 61 | distribution: 'adopt' 62 | java-version: ${{matrix.jdk}} 63 | 64 | - name: Install utplsql 65 | run: .travis/install_utplsql.sh 66 | 67 | - name: Install demo project 68 | run: .travis/install_demo_project.sh 69 | 70 | - name: Build and Test 71 | run: mvn verify jar:jar appassembler:assemble 72 | 73 | slack-workflow-status: 74 | if: always() 75 | name: Post Workflow Status To Slack 76 | needs: [ build ] 77 | runs-on: ubuntu-latest 78 | steps: 79 | - name: Slack Workflow Notification 80 | uses: Gamesight/slack-workflow-status@master 81 | with: 82 | repo_token: ${{secrets.GITHUB_TOKEN}} 83 | slack_webhook_url: ${{secrets.SLACK_WEBHOOK_URL}} 84 | name: 'Github Actions[bot]' 85 | icon_url: 'https://octodex.github.com/images/mona-the-rivetertocat.png' 86 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/reporters/LocalAssetsCoverageHTMLReporter.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli.reporters; 2 | 3 | import org.utplsql.api.compatibility.CompatibilityProxy; 4 | import org.utplsql.api.reporter.CoverageHTMLReporter; 5 | import org.utplsql.api.reporter.Reporter; 6 | import org.utplsql.api.reporter.ReporterFactory; 7 | import org.utplsql.cli.ReporterOptions; 8 | 9 | import java.nio.file.Path; 10 | import java.nio.file.Paths; 11 | import java.sql.Connection; 12 | import java.sql.SQLException; 13 | 14 | /** 15 | * Simple replacement of the CoverageHTMLReporter which writes the necessary assets to a folder 16 | * named after the Output File's name. 17 | * 18 | * @author pesse 19 | */ 20 | public class LocalAssetsCoverageHTMLReporter extends CoverageHTMLReporter implements ReporterOptionsAware { 21 | 22 | private ReporterOptions options; 23 | 24 | public LocalAssetsCoverageHTMLReporter(String selfType, Object[] attributes) { 25 | super(selfType, attributes); 26 | } 27 | 28 | @Override 29 | public Reporter init(Connection con, CompatibilityProxy compatibilityProxy, ReporterFactory reporterFactory) throws SQLException { 30 | super.init(con, compatibilityProxy, reporterFactory); 31 | 32 | if (hasOutputToFile()) { 33 | writeReportAssetsTo(getPhysicalAssetPath()); 34 | } 35 | 36 | return this; 37 | } 38 | 39 | private String getNameOfOutputFile() { 40 | Path outputPath = Paths.get(options.getOutputFileName()); 41 | return outputPath.getName(outputPath.getNameCount() - 1).toString(); 42 | } 43 | 44 | private Path getPhysicalAssetPath() { 45 | Path outputPath = Paths.get(options.getOutputFileName()); 46 | if (outputPath.getNameCount() > 1) { 47 | return outputPath.getParent().resolve(getAssetsPath()); 48 | } else { 49 | return Paths.get(getAssetsPath()); 50 | } 51 | } 52 | 53 | private void setAssetsPathFromOptions() { 54 | if (hasOutputToFile()) { 55 | setAssetsPath(getNameOfOutputFile() + "_assets/"); 56 | } 57 | } 58 | 59 | private boolean hasOutputToFile() { 60 | return (options != null && options.outputToFile()); 61 | } 62 | 63 | @Override 64 | public void setReporterOptions(ReporterOptions options) { 65 | this.options = options; 66 | setAssetsPathFromOptions(); 67 | } 68 | 69 | @Override 70 | protected void setAttributes(Object[] attributes) { 71 | super.setAttributes(attributes); 72 | setAssetsPathFromOptions(); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/RunTestRunnerTask.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.utplsql.api.DBHelper; 6 | import org.utplsql.api.TestRunner; 7 | import org.utplsql.api.exception.OracleCreateStatmenetStuckException; 8 | import org.utplsql.api.exception.SomeTestsFailedException; 9 | 10 | import javax.sql.DataSource; 11 | import java.sql.Connection; 12 | import java.sql.SQLException; 13 | import java.util.concurrent.Callable; 14 | import java.util.concurrent.Executors; 15 | 16 | /** 17 | * Runs the utPLSQL Test-Runner 18 | *

19 | * Takes care of its connection. 20 | * In case of an OracleCreateStatementStuckException it will abort the connection, otherwise close it. 21 | * 22 | * @author pesse 23 | */ 24 | public class RunTestRunnerTask implements Callable { 25 | 26 | private static final Logger logger = LoggerFactory.getLogger(RunTestRunnerTask.class); 27 | private DataSource dataSource; 28 | private TestRunner testRunner; 29 | private boolean enableDmbsOutput; 30 | 31 | RunTestRunnerTask(DataSource dataSource, TestRunner testRunner, boolean enableDmbsOutput) { 32 | this.dataSource = dataSource; 33 | this.testRunner = testRunner; 34 | this.enableDmbsOutput = enableDmbsOutput; 35 | } 36 | 37 | @Override 38 | public Boolean call() throws Exception { 39 | Connection conn = null; 40 | try { 41 | conn = dataSource.getConnection(); 42 | if (enableDmbsOutput) DBHelper.enableDBMSOutput(conn); 43 | logger.info("Running tests now."); 44 | logger.info("--------------------------------------"); 45 | testRunner.run(conn); 46 | } catch (SomeTestsFailedException e) { 47 | throw e; 48 | } catch (OracleCreateStatmenetStuckException e) { 49 | try { 50 | conn.abort(Executors.newSingleThreadExecutor()); 51 | conn = null; 52 | } catch (SQLException e1) { 53 | logger.error(e1.getMessage(), e1); 54 | } 55 | throw e; 56 | } catch (SQLException e) { 57 | System.out.println(e.getMessage()); 58 | throw e; 59 | } finally { 60 | if (conn != null) { 61 | try { 62 | if (enableDmbsOutput) DBHelper.disableDBMSOutput(conn); 63 | conn.close(); 64 | } catch (SQLException e) { 65 | logger.error(e.getMessage(), e); 66 | } 67 | } 68 | } 69 | return true; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/log/StringBlockFormatter.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli.log; 2 | 3 | public class StringBlockFormatter { 4 | 5 | private String headline; 6 | private final StringBuilder content = new StringBuilder(); 7 | 8 | public StringBlockFormatter() { 9 | } 10 | 11 | public StringBlockFormatter(String headline) { 12 | setHeadline(headline); 13 | } 14 | 15 | public void setHeadline(String headline) { 16 | this.headline = headline; 17 | } 18 | 19 | public String getHeadline() { 20 | return headline; 21 | } 22 | 23 | public void append(CharSequence seq) { 24 | content.append(seq); 25 | } 26 | 27 | public void appendLine(CharSequence seq) { 28 | content.append(seq).append("\n"); 29 | } 30 | 31 | private int getMaxLength(String[] lines) { 32 | int len = 0; 33 | for (String line : lines) { 34 | if (line.length() > len) { 35 | len = line.length(); 36 | } 37 | } 38 | 39 | if (headline.length() > (len + 6)) { 40 | len = headline.length(); 41 | } 42 | 43 | return len; 44 | } 45 | 46 | public static String getEncapsulatedLine(String line, int maxLength) { 47 | return String.format("# %-" + maxLength + "s #", line); 48 | } 49 | 50 | public static String getEncapsulatedHeadline(String headline, int maxLength) { 51 | String content = new String(new char[maxLength + 8]).replace("\0", "#"); 52 | if (headline == null || headline.isEmpty()) { 53 | return content; 54 | } 55 | 56 | headline = " " + headline + " "; 57 | int start = (int) Math.floor( 58 | (float) content.length() / 2f 59 | - (float) headline.length() / 2f 60 | ); 61 | int end = start + headline.length(); 62 | 63 | return content.substring(0, start) 64 | + headline 65 | + content.substring(end); 66 | } 67 | 68 | public String toString() { 69 | 70 | String[] lines = content.toString().split("\n"); 71 | int maxLen = getMaxLength(lines); 72 | 73 | StringBuilder sb = new StringBuilder(); 74 | 75 | sb.append(getEncapsulatedHeadline(headline, maxLen)).append("\n"); 76 | sb.append(getEncapsulatedLine("", maxLen)).append("\n"); 77 | for (String line : lines) { 78 | sb.append(getEncapsulatedLine(line, maxLen)).append("\n"); 79 | } 80 | sb.append(getEncapsulatedLine("", maxLen)).append("\n"); 81 | sb.append(getEncapsulatedHeadline("", maxLen)); 82 | 83 | return sb.toString(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/LocaleInitializer.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.utplsql.api.EnvironmentVariableUtil; 6 | 7 | import java.util.Locale; 8 | import java.util.regex.Matcher; 9 | import java.util.regex.Pattern; 10 | 11 | /** 12 | * This class makes sure the java locale is set according to the environment variables LC_ALL and LANG 13 | * We experienced that, in some cases, the locale was not set as expected, therefore this class implements some clear 14 | * rules: 15 | * 1. If environment variable LC_ALL is set, we try to parse its content and set locale according to its value if valid 16 | * 2. If environment variable LANG is set, we try to parse its content and set locale according to its value if valid 17 | * 3. Otherwise we use default locale 18 | * 19 | * @author pesse 20 | */ 21 | class LocaleInitializer { 22 | 23 | private static final Logger logger = LoggerFactory.getLogger(RunAction.class); 24 | 25 | private static final Pattern REGEX_LOCALE = Pattern.compile("^([a-zA-Z]+)[_-]([a-zA-Z]+)"); // We only need the very first part and are pretty forgiving in parsing 26 | 27 | /** 28 | * Sets the default locale according to the rules described above 29 | */ 30 | static void initLocale() { 31 | 32 | boolean localeChanged = setDefaultLocale(EnvironmentVariableUtil.getEnvValue("LC_ALL")); 33 | if (!localeChanged) { 34 | localeChanged = setDefaultLocale(EnvironmentVariableUtil.getEnvValue("LANG")); 35 | } 36 | if ( !localeChanged ) { 37 | logger.debug("Java Locale not changed from LC_ALL or LANG environment variable"); 38 | } 39 | } 40 | 41 | /** 42 | * Set the default locale from a given string like LC_ALL or LANG environment variable 43 | * 44 | * @param localeString Locale-string from LC_ALL or LANG, e.g "en_US.utf-8" 45 | * @return true if successful, false if not 46 | */ 47 | private static boolean setDefaultLocale(String localeString) { 48 | if (localeString == null || localeString.isEmpty()) { 49 | return false; 50 | } 51 | 52 | try { 53 | Matcher m = REGEX_LOCALE.matcher(localeString); 54 | if (m.find()) { 55 | StringBuilder sb = new StringBuilder(); 56 | sb.append(m.group(1)); 57 | if (m.group(2) != null) { 58 | sb.append("-").append(m.group(2)); 59 | } 60 | 61 | Locale l = new Locale.Builder().setLanguageTag(sb.toString()).build(); 62 | if (l != null) { 63 | Locale.setDefault(l); 64 | logger.debug("Java Locale changed to {}", l); 65 | return true; 66 | } 67 | } 68 | } catch (Exception e) { 69 | System.out.println("Could not get locale from " + localeString); 70 | } 71 | 72 | return false; 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/TestHelper.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.utplsql.api.DBHelper; 4 | import org.utplsql.api.EnvironmentVariableUtil; 5 | import org.utplsql.api.Version; 6 | import org.utplsql.cli.config.RunCommandConfig; 7 | import picocli.CommandLine; 8 | 9 | import javax.sql.DataSource; 10 | import java.sql.Connection; 11 | import java.sql.SQLException; 12 | import java.util.ArrayList; 13 | import java.util.Arrays; 14 | import java.util.List; 15 | 16 | class TestHelper { 17 | private static String sUrl; 18 | private static String sUser; 19 | private static String sPass; 20 | 21 | static { 22 | sUrl = EnvironmentVariableUtil.getEnvValue("DB_URL", "192.168.99.100:1521:XE"); 23 | sUser = EnvironmentVariableUtil.getEnvValue("DB_USER", "app"); 24 | sPass = EnvironmentVariableUtil.getEnvValue("DB_PASS", "app"); 25 | } 26 | 27 | static RunPicocliCommand createPicocliRunCommand(String... args ) { 28 | Object obj = new UtplsqlPicocliCommand(); 29 | CommandLine cline = new CommandLine(obj); 30 | cline.setTrimQuotes(true); 31 | List parsed = cline.parse(args); 32 | 33 | return parsed.get(1).getCommand(); 34 | } 35 | 36 | static IRunCommand createRunCommand(String... args) { 37 | ArrayList newArgs = new ArrayList<>(args.length+1); 38 | newArgs.add("run"); 39 | newArgs.addAll(Arrays.asList(args)); 40 | return createPicocliRunCommand(newArgs.toArray(new String[0])); 41 | } 42 | 43 | static RunCommandConfig parseRunConfig(String... args ) throws Exception { 44 | Object obj = new UtplsqlPicocliCommand(); 45 | CommandLine cline = new CommandLine(obj); 46 | cline.setTrimQuotes(true); 47 | List parsed = cline.parse(args); 48 | 49 | RunPicocliCommand runCmd = parsed.get(1).getCommand(); 50 | return runCmd.getRunCommandConfig(); 51 | } 52 | 53 | static RunAction createRunAction(String... args) throws Exception { 54 | ArrayList newArgs = new ArrayList<>(args.length+1); 55 | newArgs.add("run"); 56 | newArgs.addAll(Arrays.asList(args)); 57 | return new RunAction(parseRunConfig(newArgs.toArray(new String[0]))); 58 | } 59 | 60 | static int runApp(String... args) { 61 | return Cli.runPicocliWithExitCode(args); 62 | } 63 | 64 | static Version getFrameworkVersion() throws SQLException { 65 | DataSource ds = DataSourceProvider.getDataSource(TestHelper.getConnectionString(), 1); 66 | try (Connection con = ds.getConnection() ) { 67 | return DBHelper.getDatabaseFrameworkVersion(con); 68 | } 69 | } 70 | 71 | static String getConnectionString() { 72 | return sUser + "/" + sPass + "@" + sUrl; 73 | } 74 | 75 | static String getUser() { 76 | return sUser; 77 | } 78 | 79 | static String getPass() { 80 | return sPass; 81 | } 82 | 83 | static String getUrl() { 84 | return sUrl; 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: java 3 | 4 | services: 5 | - docker 6 | 7 | jdk: 8 | - openjdk8 9 | 10 | env: 11 | global: 12 | - DOCKER_CFG=$HOME/.docker 13 | - DOCKER_REPO="utplsqlv3/oracledb" 14 | - CACHE_DIR=$HOME/.cache 15 | - MAVEN_HOME=/usr/local/maven 16 | - MAVEN_CFG=$HOME/.m2 17 | - DB_URL="127.0.0.1:1521:XE" 18 | - DB_USER=app 19 | - DB_PASS=app 20 | - ORACLE_VERSION="11g-r2-xe" 21 | - DOCKER_OPTIONS="--shm-size=1g" 22 | - UTPLSQL_FILE="utPLSQL" 23 | matrix: 24 | - UTPLSQL_VERSION="v3.0.0" 25 | UTPLSQL_FILE="utPLSQLv3.0.0" 26 | - UTPLSQL_VERSION="v3.0.1" 27 | - UTPLSQL_VERSION="v3.0.2" 28 | - UTPLSQL_VERSION="v3.0.3" 29 | - UTPLSQL_VERSION="v3.0.4" 30 | - UTPLSQL_VERSION="v3.1.1" 31 | - UTPLSQL_VERSION="v3.1.2" 32 | - UTPLSQL_VERSION="v3.1.3" 33 | - UTPLSQL_VERSION="v3.1.6" 34 | - UTPLSQL_VERSION="v3.1.7" 35 | - UTPLSQL_VERSION="v3.1.8" 36 | - UTPLSQL_VERSION="develop" 37 | UTPLSQL_FILE="utPLSQL" 38 | 39 | cache: 40 | directories: 41 | - $DOCKER_CFG 42 | - $CACHE_DIR 43 | - $MAVEN_CFG 44 | 45 | install: 46 | - bash .travis/start_db.sh 47 | - bash .travis/install_utplsql.sh 48 | - bash .travis/install_demo_project.sh 49 | 50 | script: 51 | - mvn package -DskipTests 52 | - mvn package verify jar:jar appassembler:assemble 53 | 54 | before_deploy: 55 | - bash .travis/create_release.sh 56 | - if [ ! -z "$TRAVIS_TAG" ]; then VERSION=$(tr -d "/v/" <<<$TRAVIS_TAG); mvn org.codehaus.mojo:versions-maven-plugin:2.1:set -DnewVersion=${VERSION}; fi 57 | 58 | deploy: 59 | - provider: releases 60 | api_key: $GITHUB_API_TOKEN 61 | file: 62 | - utPLSQL-cli.zip 63 | - utPLSQL-cli.zip.md5 64 | skip_cleanup: true 65 | on: 66 | repository: utPLSQL/utPLSQL-cli 67 | tags: true 68 | # Use only first job "#xxx.1" to publish artifacts 69 | condition: "${TRAVIS_JOB_NUMBER} =~ \\.1$" 70 | - provider: bintray 71 | file: bintray.json 72 | user: ${BINTRAY_USER} 73 | key: ${BINTRAY_API_KEY} 74 | dry-run: false 75 | skip_cleanup: true 76 | on: 77 | repository: utPLSQL/utPLSQL-cli 78 | branch: develop 79 | # Use only first job "#xxx.1" to publish artifacts 80 | condition: "${TRAVIS_JOB_NUMBER} =~ \\.1$" 81 | 82 | notifications: 83 | slack: 84 | rooms: 85 | - secure: "jTPq4AcWQdWp1rB175c05ei/1lXZF1DBoqiKl9PPe7f8vqXS2QLPXMwGTEkZ1YnqL2MPZMB+50Gw5RhLKQ+t+9pN7ejLO0D8ULi1e96PDU5ZL5pNvRglH6U/lR5iT1CjELUuraDXDJ98Vu4gluLMiwTtFYQhNrOoA2V84L+8fF7rGjbGl4/zOXA9RQ4YAcOomJesb4vE7BCjhDjUuMW43xjNWg2s0WAiF+fC1HY/tsBMb1CGfpVULnO5ES5bKtUv/aGtySzH0vXilGDUvhZ/8mdaRn6uxpzcqrFdAyW8elSD28CypcYoxy6Myudw3SFiRPs0/Z02VXvm8DQtU/lcFEnaO7dMG+FpFhsWbrEjcRS6R7ve8uc7e/WuaQhtNVzSTOzRe+JFPGP9FOcYN+AcW2NJFILV9yT7+X/MPIB0OMxuwaPmVgtoyx0oec2nw05azmDr76P2e1XLnKtxa8ouwvPx8EMgApnXSR6VmLGu/w8nmtvIWjEAJ88cdGhwBagtyAsn2frzHq8aKpMziDRYvw4ivwGAeHJFsDtdRXzSbxhxmjl6+is3P0g0iAfojQ/Pzd4GugSYwqRQLLE7dUE0FAKXhKAAQAkjRZxik2+w6jgG8PuLuHdR4pm7C9srL+WAX8aFq/RQXE7BoIdqiZ5TMekw8d6eCRTjcRHsa3pPAd0=" 86 | on_success: change 87 | on_failure: always 88 | -------------------------------------------------------------------------------- /src/main/java/org/utplsql/cli/ReportersCommand.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.utplsql.api.exception.DatabaseNotCompatibleException; 4 | import org.utplsql.api.exception.UtPLSQLNotInstalledException; 5 | import org.utplsql.api.reporter.ReporterFactory; 6 | import org.utplsql.api.reporter.inspect.ReporterInfo; 7 | import org.utplsql.api.reporter.inspect.ReporterInspector; 8 | import org.utplsql.cli.exception.DatabaseConnectionFailed; 9 | import picocli.CommandLine.Command; 10 | import picocli.CommandLine.Option; 11 | import picocli.CommandLine.Parameters; 12 | 13 | import javax.sql.DataSource; 14 | import java.io.PrintStream; 15 | import java.sql.Connection; 16 | import java.util.Arrays; 17 | import java.util.Comparator; 18 | import java.util.List; 19 | 20 | @Command(name = "reporters", description = "prints a list of reporters available in the specified database") 21 | public class ReportersCommand implements ICommand { 22 | 23 | @Parameters(description = UtplsqlPicocliCommand.COMMANDLINE_PARAM_DESCRIPTION, arity = "1") 24 | private String connectionString; 25 | 26 | @Option(names = "-h", usageHelp = true, description = "display this help and exit") 27 | boolean help; 28 | 29 | @Override 30 | public int run() { 31 | LoggerConfiguration.configure(LoggerConfiguration.ConfigLevel.NONE); 32 | 33 | try { 34 | DataSource ds = DataSourceProvider.getDataSource(connectionString, 1); 35 | try (Connection con = ds.getConnection()) { 36 | 37 | ReporterFactory reporterFactory = ReporterFactoryProvider.createReporterFactory(con); 38 | 39 | writeReporters(ReporterInspector.create(reporterFactory, con).getReporterInfos(), System.out); 40 | } 41 | } catch (DatabaseNotCompatibleException | UtPLSQLNotInstalledException | DatabaseConnectionFailed | IllegalArgumentException e) { 42 | System.out.println(e.getMessage()); 43 | return 1; 44 | } catch (Exception e) { 45 | e.printStackTrace(); 46 | return 1; 47 | } 48 | 49 | return 0; 50 | } 51 | 52 | private void writeReporters(List reporterInfos, PrintStream out) { 53 | reporterInfos.stream() 54 | .sorted(Comparator.comparing(ReporterInfo::getName)) 55 | .forEach(info -> writeReporter(info, 4, out)); 56 | } 57 | 58 | private void writeReporter(ReporterInfo info, int padding, PrintStream out) { 59 | 60 | writeReporterName(info, padding, out); 61 | writeReporterDescription(info, padding, out); 62 | 63 | out.println(); 64 | } 65 | 66 | private void writeReporterName(ReporterInfo info, int paddingRight, PrintStream out) { 67 | out.println(info.getName() + ":"); 68 | 69 | } 70 | 71 | private void writeReporterDescription(ReporterInfo info, int paddingLeft, PrintStream out) { 72 | String[] lines = info.getDescription().split("\n"); 73 | String paddingLeftStr = String.format("%1$" + paddingLeft + "s", ""); 74 | Arrays.stream(lines).forEach(line -> out.println(paddingLeftStr + line.trim())); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/util/SystemCapturer.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli.util; 2 | 3 | import java.io.ByteArrayOutputStream; 4 | import java.io.IOException; 5 | import java.io.OutputStream; 6 | import java.io.PrintStream; 7 | import java.util.Arrays; 8 | import java.util.List; 9 | 10 | /** All credit to Manasjyoti Sharma: https://stackoverflow.com/a/30665299 11 | */ 12 | public abstract class SystemCapturer { 13 | 14 | private ByteArrayOutputStream baos; 15 | private PrintStream previous; 16 | private boolean capturing; 17 | 18 | protected abstract PrintStream getOriginalStream(); 19 | 20 | protected abstract void setSystemStream( PrintStream stream ); 21 | 22 | public void start() { 23 | if (capturing) { 24 | return; 25 | } 26 | 27 | capturing = true; 28 | previous = getOriginalStream(); 29 | baos = new ByteArrayOutputStream(); 30 | 31 | OutputStream outputStreamCombiner = 32 | new OutputStreamCombiner(Arrays.asList(previous, baos)); 33 | PrintStream custom = new PrintStream(outputStreamCombiner); 34 | 35 | setSystemStream(custom); 36 | } 37 | 38 | public String stop() { 39 | if (!capturing) { 40 | return ""; 41 | } 42 | 43 | setSystemStream(previous); 44 | 45 | String capturedValue = baos.toString(); 46 | 47 | baos = null; 48 | previous = null; 49 | capturing = false; 50 | 51 | return capturedValue; 52 | } 53 | 54 | private static class OutputStreamCombiner extends OutputStream { 55 | private List outputStreams; 56 | 57 | public OutputStreamCombiner(List outputStreams) { 58 | this.outputStreams = outputStreams; 59 | } 60 | 61 | public void write(int b) throws IOException { 62 | for (OutputStream os : outputStreams) { 63 | os.write(b); 64 | } 65 | } 66 | 67 | public void flush() throws IOException { 68 | for (OutputStream os : outputStreams) { 69 | os.flush(); 70 | } 71 | } 72 | 73 | public void close() throws IOException { 74 | for (OutputStream os : outputStreams) { 75 | os.close(); 76 | } 77 | } 78 | } 79 | 80 | public static class SystemOutCapturer extends SystemCapturer { 81 | 82 | @Override 83 | protected PrintStream getOriginalStream() { 84 | return System.out; 85 | } 86 | 87 | @Override 88 | protected void setSystemStream(PrintStream stream) { 89 | System.setOut(stream); 90 | } 91 | } 92 | 93 | public static class SystemErrCapturer extends SystemCapturer { 94 | 95 | @Override 96 | protected PrintStream getOriginalStream() { 97 | return System.err; 98 | } 99 | 100 | @Override 101 | protected void setSystemStream(PrintStream stream) { 102 | System.setErr(stream); 103 | } 104 | } 105 | } 106 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/DataSourceProviderIT.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.utplsql.cli.datasource.TestedDataSourceProvider; 5 | 6 | import javax.sql.DataSource; 7 | import java.sql.Connection; 8 | import java.sql.PreparedStatement; 9 | import java.sql.ResultSet; 10 | import java.sql.SQLException; 11 | 12 | import static org.junit.jupiter.api.Assertions.*; 13 | 14 | class DataSourceProviderIT { 15 | 16 | @Test 17 | void connectToDatabase() throws SQLException { 18 | DataSource dataSource = getDataSource(); 19 | 20 | assertNotNull(dataSource); 21 | } 22 | 23 | //@Test 24 | void connectAsSysdba() throws SQLException { 25 | ConnectionConfig config = new ConnectionConfig("sys as sysdba/oracle@localhost:1522/ORCLPDB1"); 26 | DataSource dataSource = new TestedDataSourceProvider(config, 2).getDataSource(); 27 | 28 | assertNotNull(dataSource); 29 | } 30 | 31 | @Test 32 | void initNlsLang() throws SQLException { 33 | System.setProperty("NLS_LANG", "BRAZILIAN PORTUGUESE_BRAZIL.WE8ISO8859P1"); 34 | DataSource dataSource = getDataSource(); 35 | 36 | assertNotNull(dataSource); 37 | checkNlsSessionParameter(dataSource, "NLS_LANGUAGE", "BRAZILIAN PORTUGUESE"); 38 | checkNlsSessionParameter(dataSource, "NLS_TERRITORY", "BRAZIL"); 39 | } 40 | 41 | @Test 42 | void initPartialNlsLangTerritory() throws SQLException { 43 | System.setProperty("NLS_LANG", "_SOMALIA"); 44 | DataSource dataSource = getDataSource(); 45 | 46 | assertNotNull(dataSource); 47 | checkNlsSessionParameter(dataSource, "NLS_TERRITORY", "SOMALIA"); 48 | } 49 | 50 | @Test 51 | void initPartialNlsLangLanguage() throws SQLException { 52 | System.setProperty("NLS_LANG", "HINDI"); 53 | DataSource dataSource = getDataSource(); 54 | 55 | assertNotNull(dataSource); 56 | checkNlsSessionParameter(dataSource, "NLS_LANGUAGE", "HINDI"); 57 | } 58 | 59 | @Test 60 | void initNlsLangEmpty() throws SQLException { 61 | System.setProperty("NLS_LANG", ""); 62 | DataSource dataSource = getDataSource(); 63 | 64 | assertNotNull(dataSource); 65 | } 66 | 67 | private DataSource getDataSource() throws SQLException { 68 | ConnectionConfig config = new ConnectionConfig(TestHelper.getConnectionString()); 69 | return new TestedDataSourceProvider(config, 2).getDataSource(); 70 | } 71 | 72 | private void checkNlsSessionParameter( DataSource dataSource, String parameterName, String expectedValue ) throws SQLException { 73 | try ( Connection con = dataSource.getConnection() ) { 74 | try (PreparedStatement stmt = con.prepareStatement("select value from nls_session_parameters where parameter = ?")) { 75 | stmt.setString(1, parameterName); 76 | ResultSet rs = stmt.executeQuery(); 77 | if ( rs.next() ) 78 | assertEquals(expectedValue, rs.getString(1)); 79 | else 80 | fail("Could not get NLS Session parameter value for '" + parameterName + "'"); 81 | } 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/RunCommandArgumentsTest.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.DisplayName; 4 | import org.junit.jupiter.api.Test; 5 | import org.utplsql.api.TestRunner; 6 | 7 | import java.util.ArrayList; 8 | 9 | import static org.hamcrest.MatcherAssert.assertThat; 10 | import static org.hamcrest.Matchers.contains; 11 | 12 | public class RunCommandArgumentsTest { 13 | 14 | @Test 15 | @DisplayName("All arguments are recognized") 16 | public void allArgumentsAreRecognized() { 17 | IRunCommand runCmd = TestHelper.createRunCommand(TestHelper.getConnectionString(), 18 | "-p=app", 19 | "-f=ut_sonar_test_reporter", 20 | "-o=sonar_result.xml", 21 | "-s", 22 | "--tags=tag1,tag2", 23 | "--coverage-schemes=schema1,some_other_schema", 24 | "-d", 25 | "-c", 26 | "--failure-exit-code=10", 27 | "-scc", 28 | "-t=60", 29 | "-exclude=app.betwnstr", 30 | "-include=app.betwnstr", 31 | "-source_path=src/test/resources/plsql/source", 32 | "-owner=app", 33 | "-regex_expression=\"*\"", 34 | "-type_mapping=\"sql=PACKAGE BODY\"", 35 | "-owner_subexpression=0", 36 | "-type_subexpression=0", 37 | "-name_subexpression=0", 38 | "-test_path=src/test/resources/plsql/test", 39 | "-owner=app", 40 | "-regex_expression=\"*\"", 41 | "-type_mapping=\"sql=PACKAGE BODY\"", 42 | "-owner_subexpression=0", 43 | "-type_subexpression=0", 44 | "-name_subexpression=0", 45 | "--ora-stuck-timeout=2" 46 | ); 47 | 48 | TestRunner testRunner = runCmd.newTestRunner(new ArrayList<>()); 49 | } 50 | 51 | @Test 52 | void multiplePaths() { 53 | IRunCommand runCmd = TestHelper.createRunCommand(TestHelper.getConnectionString(), 54 | "-p=app.test_betwnstr,app.test_award_bonus" 55 | ); 56 | 57 | TestRunner testRunner = runCmd.newTestRunner(new ArrayList<>()); 58 | assertThat( testRunner.getOptions().pathList, contains("app.test_betwnstr", "app.test_award_bonus") ); 59 | 60 | } 61 | 62 | @Test 63 | void provideTags() { 64 | IRunCommand runCmd = TestHelper.createRunCommand(TestHelper.getConnectionString(), 65 | "--tags=tag1,tag.2" 66 | ); 67 | 68 | TestRunner testRunner = runCmd.newTestRunner(new ArrayList<>()); 69 | assertThat( testRunner.getOptions().tags, contains("tag1", "tag.2") ); 70 | } 71 | 72 | @Test 73 | void provideCoverageSchemes() { 74 | IRunCommand runCmd = TestHelper.createRunCommand(TestHelper.getConnectionString(), 75 | "--coverage-schemes=schema-1,some_other_schema" 76 | ); 77 | 78 | TestRunner testRunner = runCmd.newTestRunner(new ArrayList<>()); 79 | assertThat( testRunner.getOptions().coverageSchemes, contains("schema-1", "some_other_schema") ); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/RunCommandIT.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.utplsql.api.compatibility.OptionalFeatures; 5 | import org.utplsql.api.reporter.CoreReporters; 6 | 7 | import java.nio.file.Paths; 8 | import java.sql.SQLException; 9 | 10 | import static org.junit.jupiter.api.Assertions.assertEquals; 11 | 12 | /** 13 | * System tests for run command. 14 | */ 15 | class RunCommandIT extends AbstractFileOutputTest { 16 | 17 | private void assertValidReturnCode(int returnCode) throws SQLException { 18 | // Only expect failure-exit-code to work on several framework versions 19 | if (OptionalFeatures.FAIL_ON_ERROR.isAvailableFor(TestHelper.getFrameworkVersion())) 20 | assertEquals(2, returnCode); 21 | else 22 | assertEquals(0, returnCode); 23 | } 24 | 25 | @Test 26 | void run_Default() throws Exception { 27 | 28 | int result = TestHelper.runApp("run", 29 | TestHelper.getConnectionString(), 30 | "-f=ut_documentation_reporter", 31 | "-s", 32 | "-c", 33 | "--failure-exit-code=2"); 34 | 35 | assertValidReturnCode(result); 36 | } 37 | 38 | @Test 39 | void run_Debug() throws Exception { 40 | 41 | int result = TestHelper.runApp("run", 42 | TestHelper.getConnectionString(), 43 | "--debug", 44 | "--failure-exit-code=2"); 45 | 46 | assertValidReturnCode(result); 47 | } 48 | 49 | @Test 50 | void run_MultipleReporters() throws Exception { 51 | 52 | String outputFileName = "output_" + System.currentTimeMillis() + ".xml"; 53 | addTempPath(Paths.get(outputFileName)); 54 | 55 | int result = TestHelper.runApp("run", 56 | TestHelper.getConnectionString(), 57 | "-f=ut_documentation_reporter", 58 | "-s", 59 | "-f=" + CoreReporters.UT_SONAR_TEST_REPORTER.name(), 60 | "-o=" + outputFileName, 61 | "-c", 62 | "--failure-exit-code=2"); 63 | 64 | assertValidReturnCode(result); 65 | } 66 | 67 | 68 | @Test 69 | void run_withDbmsOutputEnabled() throws Exception { 70 | 71 | int result = TestHelper.runApp("run", 72 | TestHelper.getConnectionString(), 73 | "-D", 74 | "--failure-exit-code=2"); 75 | 76 | assertValidReturnCode(result); 77 | } 78 | 79 | @Test 80 | void run_withOutputButNoReporterDefined() throws Exception { 81 | 82 | String outputFileName = "output_" + System.currentTimeMillis() + ".xml"; 83 | addTempPath(Paths.get(outputFileName)); 84 | 85 | int result = TestHelper.runApp("run", 86 | TestHelper.getConnectionString(), 87 | "-o=" + outputFileName, 88 | "--failure-exit-code=2"); 89 | 90 | assertValidReturnCode(result); 91 | } 92 | 93 | @Test 94 | void run_withOraStuckTimeout() throws Exception { 95 | int result = TestHelper.runApp("run", 96 | TestHelper.getConnectionString(), 97 | "--ora-stuck-timeout=2", 98 | "--failure-exit-code=2"); 99 | 100 | assertValidReturnCode(result); 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/test/java/org/utplsql/cli/RunCommandCoverageReporterIT.java: -------------------------------------------------------------------------------- 1 | package org.utplsql.cli; 2 | 3 | import org.junit.jupiter.api.Test; 4 | 5 | import java.io.File; 6 | import java.nio.file.Files; 7 | import java.nio.file.Path; 8 | import java.nio.file.Paths; 9 | import java.util.regex.Matcher; 10 | import java.util.regex.Pattern; 11 | 12 | import static org.junit.jupiter.api.Assertions.assertFalse; 13 | import static org.junit.jupiter.api.Assertions.assertTrue; 14 | 15 | /** 16 | * System tests for Code Coverage Reporter 17 | * 18 | * @author pesse 19 | */ 20 | class RunCommandCoverageReporterIT extends AbstractFileOutputTest { 21 | 22 | private static final Pattern REGEX_COVERAGE_TITLE = Pattern.compile("([a-zA-Z ]+ )?([a-zA-Z0-9\\._]+)<\\/a>"); 23 | 24 | 25 | private String getTempCoverageFileName(int counter) { 26 | 27 | return "tmpCoverage_" + System.currentTimeMillis() + "_" + counter + ".html"; 28 | } 29 | 30 | /** 31 | * Returns a random filename which does not yet exist on the local path 32 | * 33 | * @return 34 | */ 35 | private Path getTempCoverageFilePath() { 36 | 37 | int i = 1; 38 | Path p = Paths.get(getTempCoverageFileName(i)); 39 | 40 | while ((Files.exists(p) || tempPathExists(p)) && i < 100) 41 | p = Paths.get(getTempCoverageFileName(i++)); 42 | 43 | if (i >= 100) 44 | throw new IllegalStateException("Could not get temporary file for coverage output"); 45 | 46 | addTempPath(p); 47 | addTempPath(Paths.get(p.toString()+"_assets")); 48 | 49 | return p; 50 | } 51 | 52 | /** 53 | * Checks Coverage HTML Output if a given packageName is listed 54 | * 55 | * @param content 56 | * @param packageName 57 | * @return 58 | */ 59 | private boolean hasCoverageListed(String content, String packageName) { 60 | Matcher m = REGEX_COVERAGE_TITLE.matcher(content); 61 | 62 | while (m.find()) { 63 | if (packageName.equals(m.group(2))) 64 | return true; 65 | } 66 | 67 | return false; 68 | } 69 | 70 | @Test 71 | void run_CodeCoverageWithIncludeAndExclude() throws Exception { 72 | 73 | Path coveragePath = getTempCoverageFilePath(); 74 | 75 | int result = TestHelper.runApp("run", TestHelper.getConnectionString(), 76 | "-f=ut_coverage_html_reporter", "-o=" + coveragePath, "-s", "-exclude=app.award_bonus,app.betwnstr"); 77 | 78 | 79 | String content = new String(Files.readAllBytes(coveragePath)); 80 | 81 | assertTrue(hasCoverageListed(content, "app.remove_rooms_by_name")); 82 | assertFalse(hasCoverageListed(content, "app.award_bonus")); 83 | assertFalse(hasCoverageListed(content, "app.betwnstr")); 84 | 85 | } 86 | 87 | @Test 88 | void coverageReporterWriteAssetsToOutput() throws Exception { 89 | Path coveragePath = getTempCoverageFilePath(); 90 | Path coverageAssetsPath = Paths.get(coveragePath.toString() + "_assets"); 91 | 92 | TestHelper.runApp("run", TestHelper.getConnectionString(), 93 | "-f=ut_coverage_html_reporter", "-o=" + coveragePath, "-s"); 94 | 95 | // Run twice to test overriding of assets 96 | TestHelper.runApp("run", TestHelper.getConnectionString(), 97 | "-f=ut_coverage_html_reporter", "-o=" + coveragePath, "-s"); 98 | 99 | 100 | // Check application file exists 101 | File applicationJs = coverageAssetsPath.resolve(Paths.get("application.js")).toFile(); 102 | assertTrue(applicationJs.exists()); 103 | 104 | // Check correct script-part in HTML source exists 105 | String content = new String(Files.readAllBytes(coveragePath)); 106 | assertTrue(content.contains("