diff --git a/sqldev/pom.xml b/sqldev/pom.xml index f04528cb..86c63539 100644 --- a/sqldev/pom.xml +++ b/sqldev/pom.xml @@ -5,135 +5,140 @@ org.utplsql org.utplsql.sqldev - 1.1.2-SNAPSHOT + 1.2.0-SNAPSHOT bundle UTF-8 1.8 1.8 - 2.20.0 /Applications/SQLDeveloper19.4.0.app/Contents/Resources/sqldeveloper utplsql_for_SQLDev_${project.version} + + + -noverify -Djava.util.logging.config.file=${project.basedir}/src/test/resources/logging.conf - + + + + oracle idert - 12.2.0 + 13.0.0 system ${sqldev.basedir}/ide/lib/idert.jar oracle javatools-nodeps - 12.2.0 + 13.0.0 system ${sqldev.basedir}/modules/oracle.javatools/javatools-nodeps.jar oracle javatools - 12.2.0 + 13.0.0 system ${sqldev.basedir}/ide/lib/javatools.jar oracle oracle.ide.ceditor - 12.2.0 + 13.0.0 system ${sqldev.basedir}/ide/extensions/oracle.ide.ceditor.jar oracle oracle.ide - 12.2.0 + 13.0.0 system ${sqldev.basedir}/ide/extensions/oracle.ide.jar oracle uic - 12.2.2 + 13.0.0 system ${sqldev.basedir}/ide/lib/uic.jar oracle oracle.ide.navigator - 12.2.0 + 13.0.0 system ${sqldev.basedir}/ide/extensions/oracle.ide.navigator.jar oracle javax-ide - 12.2.0 + 13.0.0 system ${sqldev.basedir}/ide/lib/javax-ide.jar oracle oracle.dbtools-common - 12.2.0 + 19.4.0 system ${sqldev.basedir}/sqldeveloper/lib/dbtools-common.jar oracle oracle.sqldeveloper - 12.2.0 + 19.3.0 system ${sqldev.basedir}/sqldeveloper/extensions/oracle.sqldeveloper.jar oracle oracle.sqldeveloper.utils - 12.2.0 + 19.3.0 system ${sqldev.basedir}/sqldeveloper/extensions/oracle.sqldeveloper.utils.jar oracle oracle.sqldeveloper.worksheet - 12.2.0 + 19.3.0 system ${sqldev.basedir}/sqldeveloper/extensions/oracle.sqldeveloper.worksheet.jar oracle ojdbc8 - 12.2.0.1.0 + 19.3.0 system ${sqldev.basedir}/jdbc/lib/ojdbc8.jar oracle dbapi - 12.2.1 + 19.3.0 system ${sqldev.basedir}/ide/lib/dbapi.jar oracle oracle.ide.db - 12.2.1 + 19.3.0 system ${sqldev.basedir}/ide/extensions/oracle.ide.db.jar oracle oracle.jdeveloper.db.connection - 12.2.1 + 19.3.0 system ${sqldev.basedir}/jdev/extensions/oracle.jdeveloper.db.connection.jar oracle oracle.jdeveloper.java.core.jar - 12.2.1 + 13.0.0 system ${sqldev.basedir}/jdev/extensions/oracle.jdeveloper.java.core.jar @@ -141,14 +146,14 @@ oracle jewt4.jar - 12.2.1 + 13.0.0 system ${sqldev.basedir}/modules/oracle.bali.jewt/jewt4.jar oracle share.jar - 12.2.1 + 13.0.0 system ${sqldev.basedir}/modules/oracle.bali.share/share.jar @@ -162,22 +167,19 @@ - - org.eclipse.xtend - org.eclipse.xtend.lib - ${xtend.version} - - + org.springframework spring-jdbc - 5.2.4.RELEASE + 5.2.6.RELEASE + org.springframework spring-web - 5.2.4.RELEASE + 5.2.6.RELEASE + org.oddgen org.oddgen.sqldev 0.3.1 @@ -204,34 +206,6 @@ - - org.eclipse.xtend - xtend-maven-plugin - - ${xtend.version} - - - main - - compile - - - ${jdk.version} - ${project.basedir}/src/main/xtend-gen - - - - test - - testCompile - - - ${jdk.version.test} - ${project.basedir}/src/test/xtend-gen - - - - org.apache.maven.plugins 3.8.1 @@ -266,9 +240,7 @@ maven-surefire-plugin 2.22.2 - - -noverify - -Djava.util.logging.config.file=${project.basedir}/src/test/resources/logging.conf + **/*.java @@ -296,7 +268,7 @@ org.apache.maven.plugins maven-antrun-plugin - 1.8 + 3.0.0 prepare-package @@ -424,13 +396,13 @@ org.utplsql.sqldev.resources <_exportcontents> - org.eclipse.xtext.xbase.lib, org.aspectj.runtime.internal, org.aspectj.lang, org.aspectj.runtime, org.aspectj.lang.reflect + oracle.javatools, oracle.javatools-nodeps, oracle.jdeveloper.db.connection, oracle.idert, @@ -450,7 +422,7 @@ maven-assembly-plugin - 3.2.0 + 3.3.0 ${final.name} false @@ -472,7 +444,7 @@ net.nicoulaj.maven.plugins checksum-maven-plugin - 1.8 + 1.9 calculate-checksums @@ -483,10 +455,30 @@ + + org.jacoco + jacoco-maven-plugin + 0.8.5 + + + + prepare-agent + + + + report + prepare-package + + report + + + + + org.eclipse.m2e lifecycle-mapping 1.0.0 @@ -497,7 +489,7 @@ org.apache.maven.plugins maven-dependency-plugin - [3.1.1,) + [3.1.2,) copy-dependencies @@ -515,7 +507,7 @@ build-helper-maven-plugin - [3.0.0,) + [3.1.0,) parse-version diff --git a/sqldev/src/main/java/org/utplsql/sqldev/coverage/CodeCoverageReporter.java b/sqldev/src/main/java/org/utplsql/sqldev/coverage/CodeCoverageReporter.java new file mode 100644 index 00000000..c00a0473 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/coverage/CodeCoverageReporter.java @@ -0,0 +1,166 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.coverage; + +import java.awt.Desktop; +import java.io.File; +import java.net.URL; +import java.nio.charset.StandardCharsets; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.logging.Logger; + +import org.utplsql.sqldev.dal.UtplsqlDao; +import org.utplsql.sqldev.exception.GenericDatabaseAccessException; +import org.utplsql.sqldev.exception.GenericRuntimeException; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.FileTools; +import org.utplsql.sqldev.ui.coverage.CodeCoverageReporterDialog; + +public class CodeCoverageReporter { + private static final Logger logger = Logger.getLogger(CodeCoverageReporter.class.getName()); + + private Connection conn; + private List pathList; + private List includeObjectList; + private CodeCoverageReporterDialog frame; + private String schemas; + private String includeObjects; + private String excludeObjects; + + public CodeCoverageReporter(final List pathList, final List includeObjectList, + final String connectionName) { + this.pathList = pathList; + this.includeObjectList = includeObjectList; + setConnection(connectionName); + } + + public CodeCoverageReporter(final List pathList, final List includeObjectList, + final Connection conn) { + this.pathList = pathList; + this.includeObjectList = includeObjectList; + this.conn = conn; + } + + private void setConnection(final String connectionName) { + if (connectionName == null) { + final String msg = "Cannot initialize a CodeCoverageReporter without a ConnectionName"; + logger.severe(() -> msg); + throw new NullPointerException(); + } else { + // must be closed manually + conn = DatabaseTools.cloneConnection(connectionName); + } + } + + private ArrayList toStringList(final String s) { + final ArrayList list = new ArrayList<>(); + if (s != null && !s.isEmpty()) { + for (final String item : s.split(",")) { + if (!item.isEmpty()) { + list.add(item.trim()); + } + } + } + return list; + } + + private void run() { + logger.fine(() -> "Running code coverage reporter for " + pathList + "..."); + try { + final UtplsqlDao dal = new UtplsqlDao(conn); + final String content = dal.htmlCodeCoverage(pathList, toStringList(schemas), + toStringList(includeObjects), toStringList(excludeObjects)); + final File file = File.createTempFile("utplsql_", ".html"); + logger.fine(() -> "Writing result to " + file + "..."); + FileTools.writeFile(file.toPath(), Arrays.asList(content.split(System.lineSeparator())), StandardCharsets.UTF_8); + final URL url = file.toURI().toURL(); + logger.fine(() -> "Opening " + url.toExternalForm() + " in browser..."); + final Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null; + if (desktop != null && desktop.isSupported(Desktop.Action.BROWSE) && url != null) { + desktop.browse(url.toURI()); + logger.fine(() -> url.toExternalForm() + " opened in browser."); + } else { + logger.severe( + () -> "Could not launch " + file + "in browser. No default browser defined on this system."); + } + } catch (Exception e) { + final String msg = "Error while running code coverage for " + pathList + "."; + logger.severe(() -> msg); + throw new GenericRuntimeException(msg, e); + } finally { + try { + DatabaseTools.closeConnection(conn); + } catch (GenericDatabaseAccessException e) { + // ignore + } + if (frame != null) { + frame.exit(); + } + } + } + + public void setFrame(final CodeCoverageReporterDialog frame) { + this.frame = frame; + } + + public CodeCoverageReporterDialog getFrame() { + return frame; + } + + public Connection getConnection() { + return conn; + } + + public List getPathList() { + return pathList; + } + + public List getIncludeObjectList() { + if (includeObjectList == null) { + return new ArrayList<>(); + } else { + return includeObjectList; + } + } + + public void setSchemas(final String schemas) { + this.schemas = schemas; + } + + public void setIncludeObjects(final String includeObjects) { + this.includeObjects = includeObjects; + } + + public void setExcludeObjects(final String excludeObjects) { + this.excludeObjects = excludeObjects; + } + + public Thread runAsync() { + final Thread thread = new Thread(() -> { + run(); + }); + thread.setName("code coverage reporter"); + thread.start(); + return thread; + } + + public void showParameterWindow() { + CodeCoverageReporterDialog.createAndShow(this); + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/coverage/CodeCoverageReporter.xtend b/sqldev/src/main/java/org/utplsql/sqldev/coverage/CodeCoverageReporter.xtend deleted file mode 100644 index 1b4acb50..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/coverage/CodeCoverageReporter.xtend +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.coverage - -import java.awt.Desktop -import java.io.File -import java.net.URL -import java.nio.charset.StandardCharsets -import java.nio.file.Files -import java.nio.file.Paths -import java.sql.Connection -import java.util.ArrayList -import java.util.List -import java.util.logging.Logger -import oracle.dbtools.raptor.utils.Connections -import org.utplsql.sqldev.dal.UtplsqlDao -import org.utplsql.sqldev.ui.coverage.CodeCoverageReporterDialog - -class CodeCoverageReporter { - static val Logger logger = Logger.getLogger(CodeCoverageReporter.name); - - var Connection conn - var List pathList - var List includeObjectList - var CodeCoverageReporterDialog frame - var String schemas - var String includeObjects - var String excludeObjects - - new(List pathList, List includeObjectList, String connectionName) { - this.pathList = pathList - this.includeObjectList = includeObjectList - setConnection(connectionName) - } - - new(List pathList, List includeObjectList, Connection conn) { - this.pathList = pathList - this.includeObjectList = includeObjectList - this.conn = conn - } - - private def setConnection(String connectionName) { - if (connectionName === null) { - throw new RuntimeException("Cannot initialize a CodeCoverageReporter without a ConnectionName") - } else { - // must be closed manually - this.conn = Connections.instance.cloneConnection(Connections.instance.getConnection(connectionName)) - } - } - - private def toStringList(String s) { - val list = new ArrayList - if (s !== null && !s.empty) { - for (item : s.split(",")) { - if (!item.empty) { - list.add(item.trim) - } - } - } - return list - } - - private def void run() { - try { - logger.fine('''Running code coverage reporter for «pathList»...''') - val dal = new UtplsqlDao(conn) - val content = dal.htmlCodeCoverage(pathList, toStringList(schemas), toStringList(includeObjects), toStringList(excludeObjects)) - val file = File.createTempFile("utplsql_", ".html") - logger.fine('''Writing result to «file.absolutePath»...''') - Files.write(Paths.get(file.absolutePath), content.split(System.lineSeparator), StandardCharsets.UTF_8); - val url = file.toURI().toURL().toExternalForm() - logger.fine('''Opening «url» in browser...''') - val Desktop desktop = if (Desktop.isDesktopSupported()) {Desktop.getDesktop()} else {null} - if (desktop !== null && desktop.isSupported(Desktop.Action.BROWSE) && url !== null) { - desktop.browse((new URL(url)).toURI) - logger.fine(url + " opened in browser."); - } else { - logger.severe('''Could not launch «file» in browser. No default browser defined on this system.''') - } - } catch (Exception e) { - logger.severe('''Error when running code coverage: «e?.message»''') - } - finally { - conn.close - if (frame !== null) { - frame.exit - } - } - } - - def setFrame(CodeCoverageReporterDialog frame) { - this.frame = frame; - } - - def getFrame() { - return this.frame - } - - def getConnection() { - return conn - } - - def getPathList() { - return pathList - } - - def getIncludeObjectList() { - if (includeObjectList === null) { - return new ArrayList - } else { - return includeObjectList - } - } - - def setSchemas(String schemas) { - this.schemas = schemas - } - - def setIncludeObjects(String includeObjects) { - this.includeObjects = includeObjects - } - - def setExcludeObjects(String excludeObjects) { - this.excludeObjects = excludeObjects - } - - def Thread runAsync() { - val Runnable runnable = [|run] - val thread = new Thread(runnable) - thread.name = "code coverage reporter" - thread.start - return thread - } - - def showParameterWindow() { - CodeCoverageReporterDialog.createAndShow(this) - } - -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterDao.java b/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterDao.java new file mode 100644 index 00000000..87be783c --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterDao.java @@ -0,0 +1,306 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.dal; + +import java.io.IOException; +import java.io.StringReader; +import java.sql.CallableStatement; +import java.sql.Clob; +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.util.List; +import java.util.logging.Logger; + +import javax.xml.parsers.DocumentBuilder; + +import org.springframework.jdbc.core.CallableStatementCallback; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.SingleConnectionDataSource; +import org.utplsql.sqldev.exception.GenericRuntimeException; +import org.utplsql.sqldev.model.StringTools; +import org.utplsql.sqldev.model.XMLTools; +import org.utplsql.sqldev.model.runner.Counter; +import org.utplsql.sqldev.model.runner.Expectation; +import org.utplsql.sqldev.model.runner.PostEvent; +import org.utplsql.sqldev.model.runner.PostRunEvent; +import org.utplsql.sqldev.model.runner.PostSuiteEvent; +import org.utplsql.sqldev.model.runner.PostTestEvent; +import org.utplsql.sqldev.model.runner.PreRunEvent; +import org.utplsql.sqldev.model.runner.PreSuiteEvent; +import org.utplsql.sqldev.model.runner.PreTestEvent; +import org.utplsql.sqldev.model.runner.RealtimeReporterEvent; +import org.utplsql.sqldev.model.runner.Suite; +import org.utplsql.sqldev.model.runner.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +import oracle.jdbc.OracleTypes; + +public class RealtimeReporterDao { + private static final Logger logger = Logger.getLogger(RealtimeReporterDao.class.getName()); + private static final int FIRST_VERSION_WITH_REALTIME_REPORTER = 3001004; + private final XMLTools xmlTools = new XMLTools(); + private Connection conn; + private JdbcTemplate jdbcTemplate; + + public RealtimeReporterDao(final Connection conn) { + this.conn = conn; + jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(conn, true)); + jdbcTemplate.setFetchSize(1); + } + + public boolean isSupported() { + return new UtplsqlDao(conn) + .normalizedUtPlsqlVersionNumber() >= RealtimeReporterDao.FIRST_VERSION_WITH_REALTIME_REPORTER; + } + + public void produceReport(final String reporterId, final List pathList) { + StringBuilder sb = new StringBuilder(); + sb.append("DECLARE\n"); + sb.append(" l_reporter ut_realtime_reporter := ut_realtime_reporter();\n"); + sb.append("BEGIN\n"); + sb.append(" l_reporter.set_reporter_id(?);\n"); + sb.append(" l_reporter.output_buffer.init();\n"); + sb.append(" sys.dbms_output.enable(NULL);\n"); + sb.append(" ut_runner.run(\n"); + sb.append(" a_paths => ut_varchar2_list(\n"); + sb.append(StringTools.getCSV(pathList, 24)); + sb.append(" ),\n"); + sb.append(" a_reporters => ut_reporters(l_reporter)\n"); + sb.append(" );\n"); + sb.append(" sys.dbms_output.disable;\n"); + sb.append("END;"); + final String plsql = sb.toString(); + final Object[] binds = { reporterId }; + jdbcTemplate.update(plsql, binds); + } + + public void consumeReport(final String reporterId, final RealtimeReporterEventConsumer consumer) { + StringBuilder sb = new StringBuilder(); + sb.append("DECLARE\n"); + sb.append(" l_reporter ut_realtime_reporter := ut_realtime_reporter();\n"); + sb.append("BEGIN\n"); + sb.append(" l_reporter.set_reporter_id(?);\n"); + sb.append(" ? := l_reporter.get_lines_cursor();\n"); + sb.append("END;"); + final String plsql = sb.toString(); + jdbcTemplate.execute(plsql, new CallableStatementCallback() { + @Override + public Void doInCallableStatement(final CallableStatement cs) throws SQLException { + cs.setString(1, reporterId); + cs.registerOutParameter(2, OracleTypes.CURSOR); + cs.execute(); + final ResultSet rs = ((ResultSet) cs.getObject(2)); + while (rs.next()) { + final String itemType = rs.getString("item_type"); + final Clob textClob = rs.getClob("text"); + final String textString = textClob.getSubString(1, ((int) textClob.length())); + final RealtimeReporterEvent event = convert(itemType, textString); + if (event != null) { + consumer.process(event); + } + } + rs.close(); + return null; + } + }); + } + + private RealtimeReporterEvent convert(final String itemType, final String text) { + logger.fine(() -> "\n---- " + itemType + " ----\n" + text); + try { + final DocumentBuilder docBuilder = xmlTools.createDocumentBuilder(); + final Document doc = docBuilder.parse(new InputSource(new StringReader(text))); + RealtimeReporterEvent event = null; + if ("pre-run".equals(itemType)) { + event = convertToPreRunEvent(doc); + } else if ("post-run".equals(itemType)) { + event = convertToPostRunEvent(doc); + } else if ("pre-suite".equals(itemType)) { + event = convertToPreSuiteEvent(doc); + } else if ("post-suite".equals(itemType)) { + event = convertToPostSuiteEvent(doc); + } else if ("pre-test".equals(itemType)) { + event = convertToPreTestEvent(doc); + } else if ("post-test".equals(itemType)) { + event = convertToPostTestEvent(doc); + } + return event; + } catch (SAXException e) { + final String msg = "Parse error while processing " + itemType + " with content: " + text; + logger.severe(() -> msg); + throw new GenericRuntimeException(msg, e); + } catch (IOException e) { + final String msg = "I/O error while processing " + itemType + " with content: " + text; + logger.severe(() -> msg); + throw new GenericRuntimeException(msg, e); + } + } + + private RealtimeReporterEvent convertToPreRunEvent(final Document doc) { + final PreRunEvent event = new PreRunEvent(); + final Node totalNumberOfTestsNode = xmlTools.getNode(doc, "/event/totalNumberOfTests"); + String totalNumberOfTestsTextContent = null; + if (totalNumberOfTestsNode != null) { + totalNumberOfTestsTextContent = totalNumberOfTestsNode.getTextContent(); + } + event.setTotalNumberOfTests(Integer.valueOf(totalNumberOfTestsTextContent)); + final NodeList nodes = xmlTools.getNodeList(doc, "/event/items/*"); + for (int i = 0; i < nodes.getLength(); i++) { + final Node node = nodes.item(i); + final String nodeName = node.getNodeName(); + if ("suite".equals(nodeName)) { + final Suite suite = new Suite(); + event.getItems().add(suite); + populate(suite, node); + } else if ("test".equals(nodeName)) { + final Test test = new Test(); + event.getItems().add(test); + populate(test, node); + } + } + return event; + } + + private RealtimeReporterEvent convertToPostRunEvent(final Document doc) { + final PostRunEvent event = new PostRunEvent(); + populate(event, xmlTools.getNode(doc, "/event/run")); + return event; + } + + private RealtimeReporterEvent convertToPreSuiteEvent(final Document doc) { + final PreSuiteEvent event = new PreSuiteEvent(); + final Node node = xmlTools.getNode(doc, "/event/suite"); + if (node instanceof Element) { + event.setId(xmlTools.getAttributeValue(node, "id")); + } + return event; + } + + private RealtimeReporterEvent convertToPostSuiteEvent(final Document doc) { + final PostSuiteEvent event = new PostSuiteEvent(); + final Node node = xmlTools.getNode(doc, "/event/suite"); + if (node instanceof Element) { + event.setId(xmlTools.getAttributeValue(node, "id")); + populate(event, node); + } + return event; + } + + private RealtimeReporterEvent convertToPreTestEvent(final Document doc) { + final PreTestEvent event = new PreTestEvent(); + final Node node = xmlTools.getNode(doc, "/event/test"); + if (node instanceof Element) { + event.setId(xmlTools.getAttributeValue(node, "id")); + event.setTestNumber(Integer.valueOf(xmlTools.getElementValue(node, "testNumber"))); + event.setTotalNumberOfTests(Integer.valueOf(xmlTools.getElementValue(node, "totalNumberOfTests"))); + } + return event; + } + + private RealtimeReporterEvent convertToPostTestEvent(final Document doc) { + final PostTestEvent event = new PostTestEvent(); + final Node node = xmlTools.getNode(doc, "/event/test"); + if (node instanceof Element) { + event.setId(xmlTools.getAttributeValue(node, "id")); + event.setTestNumber(Integer.valueOf(xmlTools.getElementValue(node, "testNumber"))); + event.setTotalNumberOfTests(Integer.valueOf(xmlTools.getElementValue(node, "totalNumberOfTests"))); + populate(event, node); + final NodeList failedExpectations = xmlTools.getNodeList(node, "failedExpectations/expectation"); + for (int i = 0; i < failedExpectations.getLength(); i++) { + final Node expectationNode = failedExpectations.item(i); + final Expectation expectation = new Expectation(); + event.getFailedExpectations().add(expectation); + populate(expectation, expectationNode); + } + } + return event; + } + + private void populate(final Suite suite, final Node node) { + if (node instanceof Element) { + suite.setId(xmlTools.getAttributeValue(node, "id")); + suite.setName(xmlTools.getElementValue(node, "name")); + suite.setDescription(xmlTools.getElementValue(node, "description")); + final NodeList nodeList = xmlTools.getNodeList(node, "items/*"); + for (int i = 0; i < nodeList.getLength(); i++) { + final Node childNode = nodeList.item(i); + final String nodeName = childNode.getNodeName(); + if ("suite".equals(nodeName)) { + final Suite childSuite = new Suite(); + suite.getItems().add(childSuite); + populate(childSuite, childNode); + } else if ("test".equals(nodeName)) { + final Test childTest = new Test(); + suite.getItems().add(childTest); + populate(childTest, childNode); + } + } + } + } + + private void populate(final Test test, final Node node) { + if (node instanceof Element) { + test.setId(xmlTools.getAttributeValue(node, "id")); + test.setExecutableType(xmlTools.getElementValue(node, "executableType")); + test.setOwnerName(xmlTools.getElementValue(node, "ownerName")); + test.setObjectName(xmlTools.getElementValue(node, "objectName")); + test.setProcedureName(xmlTools.getElementValue(node, "procedureName")); + test.setDisabled("true".equals(xmlTools.getElementValue(node, "disabled"))); + test.setName(xmlTools.getElementValue(node, "name")); + test.setDescription(xmlTools.getElementValue(node, "description")); + test.setTestNumber(Integer.valueOf(xmlTools.getElementValue(node, "testNumber"))); + } + } + + private void populate(final PostEvent event, final Node node) { + if (node instanceof Element) { + event.setStartTime(xmlTools.getElementValue(node, "startTime")); + event.setEndTime(xmlTools.getElementValue(node, "endTime")); + event.setExecutionTime(Double.valueOf(xmlTools.getElementValue(node, "executionTime"))); + populate(event.getCounter(), node); + event.setErrorStack(xmlTools.getElementValue(node, "errorStack")); + event.setServerOutput(xmlTools.getElementValue(node, "serverOutput")); + event.setWarnings(xmlTools.getElementValue(node, "warnings")); + } + } + + private void populate(final Counter counter, final Node node) { + if (node instanceof Element) { + final Node counterNode = xmlTools.getElementNode(node, "counter"); + if (counterNode instanceof Element) { + counter.setDisabled(Integer.valueOf(xmlTools.getElementValue(counterNode, "disabled"))); + counter.setSuccess(Integer.valueOf(xmlTools.getElementValue(counterNode, "success"))); + counter.setFailure(Integer.valueOf(xmlTools.getElementValue(counterNode, "failure"))); + counter.setError(Integer.valueOf(xmlTools.getElementValue(counterNode, "error"))); + counter.setWarning(Integer.valueOf(xmlTools.getElementValue(counterNode, "warning"))); + } + } + } + + private void populate(final Expectation expectation, final Node node) { + if (node instanceof Element) { + expectation.setDescription(xmlTools.getElementValue(node, "description")); + expectation.setMessage(xmlTools.getElementValue(node, "message")); + expectation.setCaller(xmlTools.getElementValue(node, "caller")); + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterDao.xtend b/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterDao.xtend deleted file mode 100644 index 442027ac..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterDao.xtend +++ /dev/null @@ -1,283 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.utplsql.sqldev.dal - -import java.io.StringReader -import java.sql.CallableStatement -import java.sql.Connection -import java.sql.ResultSet -import java.sql.SQLException -import java.util.List -import java.util.logging.Logger -import javax.xml.parsers.DocumentBuilderFactory -import oracle.jdbc.OracleTypes -import org.springframework.dao.DataAccessException -import org.springframework.jdbc.core.CallableStatementCallback -import org.springframework.jdbc.core.JdbcTemplate -import org.springframework.jdbc.datasource.SingleConnectionDataSource -import org.utplsql.sqldev.model.XMLTools -import org.utplsql.sqldev.model.runner.Counter -import org.utplsql.sqldev.model.runner.Expectation -import org.utplsql.sqldev.model.runner.PostEvent -import org.utplsql.sqldev.model.runner.PostRunEvent -import org.utplsql.sqldev.model.runner.PostSuiteEvent -import org.utplsql.sqldev.model.runner.PostTestEvent -import org.utplsql.sqldev.model.runner.PreRunEvent -import org.utplsql.sqldev.model.runner.PreSuiteEvent -import org.utplsql.sqldev.model.runner.PreTestEvent -import org.utplsql.sqldev.model.runner.RealtimeReporterEvent -import org.utplsql.sqldev.model.runner.Suite -import org.utplsql.sqldev.model.runner.Test -import org.w3c.dom.Document -import org.w3c.dom.Element -import org.w3c.dom.Node -import org.xml.sax.InputSource - -class RealtimeReporterDao { - static val Logger logger = Logger.getLogger(RealtimeReporterDao.name); - static val FIRST_VERSION_WITH_REALTIME_REPORTER = 3001004 - val extension XMLTools xmlTools = new XMLTools - var Connection conn - var JdbcTemplate jdbcTemplate - - new(Connection connection) { - conn = connection - jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(conn, true)) - jdbcTemplate.fetchSize = 1 - } - - def isSupported() { - return new UtplsqlDao(conn).normalizedUtPlsqlVersionNumber >= FIRST_VERSION_WITH_REALTIME_REPORTER - } - - def produceReport(String reporterId, List pathList) { - var plsql = ''' - DECLARE - l_reporter ut_realtime_reporter := ut_realtime_reporter(); - BEGIN - l_reporter.set_reporter_id(?); - l_reporter.output_buffer.init(); - sys.dbms_output.enable(NULL); - ut_runner.run( - a_paths => ut_varchar2_list( - «FOR path : pathList SEPARATOR ","» - '«path»' - «ENDFOR» - ), - a_reporters => ut_reporters(l_reporter) - ); - sys.dbms_output.disable; - END; - ''' - jdbcTemplate.update(plsql, #[reporterId]) - } - - def consumeReport(String reporterId, RealtimeReporterEventConsumer consumer) { - val plsql = ''' - DECLARE - l_reporter ut_realtime_reporter := ut_realtime_reporter(); - BEGIN - l_reporter.set_reporter_id(?); - ? := l_reporter.get_lines_cursor(); - END; - ''' - jdbcTemplate.execute(plsql, new CallableStatementCallback() { - override doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException { - cs.setString(1, reporterId) - cs.registerOutParameter(2, OracleTypes.CURSOR) - cs.execute - val rs = cs.getObject(2) as ResultSet - while(rs.next) { - val itemType = rs.getString("item_type") - val textClob = rs.getClob("text") - val textString = textClob.getSubString(1, textClob.length as int) - val event = convert(itemType, textString) - if (event !== null) { - consumer.process(event) - } - } - rs.close - return null - } - }) - } - - private def RealtimeReporterEvent convert(String itemType, String text) { - logger.fine(''' - ---- «itemType» ---- - «text» - ''') - val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder() - val doc = docBuilder.parse(new InputSource(new StringReader(text))) - var RealtimeReporterEvent event - if (itemType == "pre-run") { - event = doc.convertToPreRunEvent - } else if (itemType == "post-run") { - event = doc.convertToPostRunEvent - } else if (itemType == "pre-suite") { - event = doc.convertToPreSuiteEvent - } else if (itemType == "post-suite") { - event = doc.convertToPostSuiteEvent - } else if (itemType == "pre-test") { - event = doc.convertToPreTestEvent - } else if (itemType == "post-test") { - event = doc.convertToPostTestEvent - } - return event - } - - private def RealtimeReporterEvent convertToPreRunEvent(Document doc) { - val event = new PreRunEvent - event.totalNumberOfTests = Integer.valueOf(doc.getNode("/event/totalNumberOfTests")?.textContent) - val nodeList = doc.getNodeList("/event/items/*") - for (i : 0 ..< nodeList.length) { - val node = nodeList.item(i) - if (node.nodeName == "suite") { - val suite = new Suite - event.items.add(suite) - suite.populate(node) - } else if (node.nodeName == "test") { - val test = new Test - event.items.add(test) - test.populate(node) - } - } - return event - } - - private def RealtimeReporterEvent convertToPostRunEvent(Document doc) { - val event = new PostRunEvent - event.populate(doc.getNode("/event/run")) - return event - } - - private def RealtimeReporterEvent convertToPreSuiteEvent(Document doc) { - val event = new PreSuiteEvent - val node = doc.getNode("/event/suite") - if (node instanceof Element) { - event.id = node.attributes?.getNamedItem("id")?.nodeValue - } - return event - } - - private def RealtimeReporterEvent convertToPostSuiteEvent(Document doc) { - val event = new PostSuiteEvent - val node = doc.getNode("/event/suite") - if (node instanceof Element) { - event.id = node.attributes?.getNamedItem("id")?.nodeValue - event.populate(node) - } - return event - } - - private def RealtimeReporterEvent convertToPreTestEvent(Document doc) { - val event = new PreTestEvent - val node = doc.getNode("/event/test") - if (node instanceof Element) { - event.id = node.attributes?.getNamedItem("id")?.nodeValue - event.testNumber = Integer.valueOf(node.getElementsByTagName("testNumber")?.item(0)?.textContent) - event.totalNumberOfTests = Integer.valueOf(node.getElementsByTagName("totalNumberOfTests")?.item(0)?.textContent) - } - return event - } - - private def RealtimeReporterEvent convertToPostTestEvent(Document doc) { - val event = new PostTestEvent - val node = doc.getNode("/event/test") - if (node instanceof Element) { - event.id = node.attributes?.getNamedItem("id")?.nodeValue - event.testNumber = Integer.valueOf(node.getElementsByTagName("testNumber")?.item(0)?.textContent) - event.totalNumberOfTests = Integer.valueOf(node.getElementsByTagName("totalNumberOfTests")?.item(0)?.textContent) - event.populate(node) - val failedExpectations = node.getNodeList("failedExpectations/expectation") - for (i : 0 ..< failedExpectations.length) { - val expectationNode = failedExpectations.item(i) - val expectation = new Expectation - event.failedExpectations.add(expectation) - expectation.populate(expectationNode) - } - } - return event - } - - private def void populate(Suite suite, Node node) { - if (node instanceof Element) { - suite.id = node.attributes?.getNamedItem("id")?.nodeValue - suite.name = node.getElementsByTagName("name")?.item(0)?.textContent - suite.description = node.getElementsByTagName("description")?.item(0)?.textContent - val nodeList = node.getNodeList("items/*") - for (i : 0 ..< nodeList.length) { - val childNode = nodeList.item(i) - if (childNode.nodeName == "suite") { - val childSuite = new Suite - suite.items.add(childSuite) - childSuite.populate(childNode) - } else if (childNode.nodeName == "test") { - val childTest = new Test - suite.items.add(childTest) - childTest.populate(childNode) - } - } - } - } - - private def void populate(Test test, Node node) { - if (node instanceof Element) { - test.id = node.attributes?.getNamedItem("id")?.nodeValue - test.executableType = node.getElementsByTagName("executableType")?.item(0)?.textContent - test.ownerName = node.getElementsByTagName("ownerName")?.item(0)?.textContent - test.objectName = node.getElementsByTagName("objectName")?.item(0)?.textContent - test.procedureName = node.getElementsByTagName("procedureName")?.item(0)?.textContent - test.disabled = node.getElementsByTagName("disabled")?.item(0)?.textContent == "true" - test.name = node.getElementsByTagName("name")?.item(0)?.textContent - test.description = node.getElementsByTagName("description")?.item(0)?.textContent - test.testNumber = Integer.valueOf(node.getElementsByTagName("testNumber")?.item(0)?.textContent) - } - } - - private def void populate(PostEvent event, Node node) { - if (node instanceof Element) { - event.startTime = node.getElementsByTagName("startTime")?.item(0)?.textContent - event.endTime = node.getElementsByTagName("endTime")?.item(0)?.textContent - event.executionTime = Double.valueOf(node.getElementsByTagName("executionTime")?.item(0)?.textContent) - event.counter.populate(node) - event.errorStack = node.getElementsByTagName("errorStack")?.item(0)?.textContent - event.serverOutput = node.getElementsByTagName("serverOutput")?.item(0)?.textContent - event.warnings = node.getElementsByTagName("warnings")?.item(0)?.textContent - } - } - - private def void populate(Counter counter, Node node) { - if (node instanceof Element) { - val counterNode = node.getElementsByTagName("counter")?.item(0) - if (counterNode instanceof Element) { - counter.disabled = Integer.valueOf(counterNode.getElementsByTagName("disabled")?.item(0)?.textContent) - counter.success = Integer.valueOf(counterNode.getElementsByTagName("success")?.item(0)?.textContent) - counter.failure = Integer.valueOf(counterNode.getElementsByTagName("failure")?.item(0)?.textContent) - counter.error = Integer.valueOf(counterNode.getElementsByTagName("error")?.item(0)?.textContent) - counter.warning = Integer.valueOf(counterNode.getElementsByTagName("warning")?.item(0)?.textContent) - } - } - } - - private def void populate(Expectation expectation, Node node) { - if (node instanceof Element) { - expectation.description = node.getElementsByTagName("description")?.item(0)?.textContent - expectation.message = node.getElementsByTagName("message")?.item(0)?.textContent - expectation.caller = node.getElementsByTagName("caller")?.item(0)?.textContent - } - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/AbstractModel.xtend b/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterEventConsumer.java similarity index 75% rename from sqldev/src/main/java/org/utplsql/sqldev/model/AbstractModel.xtend rename to sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterEventConsumer.java index f4356033..65427f35 100644 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/AbstractModel.xtend +++ b/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterEventConsumer.java @@ -13,12 +13,10 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.utplsql.sqldev.model +package org.utplsql.sqldev.dal; -import org.eclipse.xtext.xbase.lib.util.ToStringBuilder +import org.utplsql.sqldev.model.runner.RealtimeReporterEvent; -abstract class AbstractModel { - override toString() { - new ToStringBuilder(this).addAllFields.toString - } +public interface RealtimeReporterEventConsumer { + public abstract void process(final RealtimeReporterEvent event); } diff --git a/sqldev/src/main/java/org/utplsql/sqldev/dal/UtplsqlDao.java b/sqldev/src/main/java/org/utplsql/sqldev/dal/UtplsqlDao.java new file mode 100644 index 00000000..be4d6d09 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/dal/UtplsqlDao.java @@ -0,0 +1,1064 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.dal; + +import java.sql.CallableStatement; +import java.sql.Connection; +import java.sql.SQLException; +import java.sql.Types; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.oddgen.sqldev.generators.model.Node; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.BeanPropertyRowMapper; +import org.springframework.jdbc.core.CallableStatementCallback; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.SingleConnectionDataSource; +import org.utplsql.sqldev.model.StringTools; +import org.utplsql.sqldev.model.ut.Annotation; +import org.utplsql.sqldev.model.ut.OutputLines; + +public class UtplsqlDao { + public static final String UTPLSQL_PACKAGE_NAME = "UT"; + public static final int NOT_INSTALLED = 0; + public static final int FIRST_VERSION_WITH_INTERNAL_ANNOTATION_API = 3000004; + public static final int FIRST_VERSION_WITH_ANNOTATION_API = 3001003; + public static final int FIRST_VERSION_WITHOUT_INTERNAL_API = 3001008; + public static final int FIRST_VERSION_WITH_HAS_SUITES_API = 3001008; + private JdbcTemplate jdbcTemplate; + // cache fields + private Boolean cachedDbaViewAccessible; + private String cachedUtplsqlSchema; + private String cachedUtPlsqlVersion; + + public UtplsqlDao(final Connection conn) { + jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(conn, true)); + } + + /** + * used for testing purposes only + */ + public void setUtPlsqlVersion(final String utPlsqlVersion) { + this.cachedUtPlsqlVersion = utPlsqlVersion; + } + + /** + * returns a normalized utPLSQL version in format 9.9.9 + */ + public String normalizedUtPlsqlVersion() { + final String version = this.getUtPlsqlVersion(); + if (version != null) { + final Pattern p = Pattern.compile("(\\d+\\.\\d+\\.\\d+)"); + final Matcher m = p.matcher(version); + if (m.find()) { + return m.group(0); + } + } + return "0.0.0"; + } + + /** + * get version as number, e.g. 3001004 + */ + public int normalizedUtPlsqlVersionNumber() { + final Pattern p = Pattern.compile("(\\d+)"); + final String version = this.normalizedUtPlsqlVersion(); + final Matcher m = p.matcher(version); + m.find(); + final String major = m.group(); + m.find(); + final String minor = m.group(); + m.find(); + final String bugfix = m.group(); + return Integer.valueOf(major) * 1000000 + Integer.valueOf(minor) * 1000 + Integer.valueOf(bugfix); + } + + /** + * gets version of installed utPLSQL + */ + public String getUtPlsqlVersion() { + if (cachedUtPlsqlVersion == null) { + final StringBuilder sb = new StringBuilder(); + sb.append("BEGIN\n"); + sb.append(" ? := ut.version;\n"); + sb.append("END;"); + final String sql = sb.toString(); + try { + cachedUtPlsqlVersion = jdbcTemplate.execute(sql, new CallableStatementCallback() { + @Override + public String doInCallableStatement(final CallableStatement cs) throws SQLException { + cs.registerOutParameter(1, Types.VARCHAR); + cs.execute(); + return cs.getString(1); + } + }); + } catch (DataAccessException e) { + // ignore error + } + } + return cachedUtPlsqlVersion; + } + + public boolean isDbaViewAccessible() { + if (cachedDbaViewAccessible == null) { + try { + final StringBuilder sb = new StringBuilder(); + sb.append("SELECT 1 AS dummy\n"); + sb.append(" FROM dba_objects\n"); + sb.append(" WHERE 1=2\n"); + sb.append("UNION ALL\n"); + sb.append("SELECT 1\n"); + sb.append(" FROM dba_synonyms\n"); + sb.append(" WHERE 1=2\n"); + sb.append("UNION ALL\n"); + sb.append("SELECT 1\n"); + sb.append(" FROM dba_dependencies\n"); + sb.append(" WHERE 1=2\n"); + final String sql = sb.toString(); + jdbcTemplate.execute(sql); + cachedDbaViewAccessible = true; + } catch (DataAccessException e) { + cachedDbaViewAccessible = false; + } + } + return cachedDbaViewAccessible.booleanValue(); + } + + public String getDbaView(String viewName) { + StringBuilder sb = new StringBuilder(); + if (isDbaViewAccessible()) { + sb.append("dba"); + } else { + sb.append("all"); + } + sb.append("_"); + sb.append(viewName); + return sb.toString(); + } + + /** + * Gets the schema name of the utPLSQL installation. + * + * @return utPLSQL schema or null if no utPLSQL is not installed + * @throws DataAccessException + * if there is a problem + */ + public String getUtplsqlSchema() { + if (cachedUtplsqlSchema == null) { + final StringBuilder sb = new StringBuilder(); + sb.append("SELECT table_owner\n"); + sb.append(" FROM "); + sb.append(getDbaView("synonyms\n")); + sb.append(" WHERE owner = 'PUBLIC'\n"); + sb.append(" AND synonym_name = '"); + sb.append(UtplsqlDao.UTPLSQL_PACKAGE_NAME); + sb.append("'\n"); + sb.append(" AND table_name = '"); + sb.append(UtplsqlDao.UTPLSQL_PACKAGE_NAME); + sb.append("'"); + final String sql = sb.toString(); + try { + final String schema = jdbcTemplate.queryForObject(sql, String.class); + cachedUtplsqlSchema = schema; + } catch (EmptyResultDataAccessException e) { + cachedUtplsqlSchema = null; + } + } + return cachedUtplsqlSchema; + } + + /** + * Checks if the package ut_annotation_manager is installed. This package has + * been introduced with utPLSQL 3.0.4. This version is a prerequisite to + * identify utPLSQL unit test procedures. + * + * @return true if ut_annotation_manager package has been found + * @throws DataAccessException + * if there is a problem + */ + public boolean isUtAnnotationManagerInstalled() { + return normalizedUtPlsqlVersionNumber() >= UtplsqlDao.FIRST_VERSION_WITH_INTERNAL_ANNOTATION_API; + } + + /** + * Checks if utPLSQL tests exist + * + * @param owner + * schema name, mandatory, case-insensitive + * @param objectName + * name of the package or package body, optional, case-insensitive + * @param subobjectName + * name of the procedure, optional, case-insensitive + * @return true if at least one test has been found + * @throws DataAccessException + * if a utPLSQL version less than 3.0.4 is installed or if there are + * other problems + */ + public boolean containsUtplsqlTest(final String owner, final String objectName, final String subobjectName) { + try { + if (normalizedUtPlsqlVersionNumber() >= UtplsqlDao.FIRST_VERSION_WITH_HAS_SUITES_API && objectName != null + && subobjectName != null) { + // use faster check function available since v3.1.3 (reliable in v3.1.8) + final StringBuilder sb = new StringBuilder(); + sb.append("DECLARE\n"); + sb.append(" l_return VARCHAR2(1) := '0';\n"); + sb.append("BEGIN\n"); + sb.append(" IF ut_runner.is_test(?, ?, ?) THEN\n"); + sb.append(" l_return := '1';\n"); + sb.append(" END IF;\n"); + sb.append(" ? := l_return;\n"); + sb.append("END;"); + final String sql = sb.toString(); + return jdbcTemplate.execute(sql, new CallableStatementCallback() { + @Override + public Boolean doInCallableStatement(final CallableStatement cs) throws SQLException { + cs.setString(1, owner); + cs.setString(2, objectName); + cs.setString(3, subobjectName); + cs.registerOutParameter(4, Types.VARCHAR); + cs.execute(); + final String ret = cs.getString(4); + return "1".equals(ret); + } + }); + } else if (normalizedUtPlsqlVersionNumber() >= FIRST_VERSION_WITH_ANNOTATION_API) { + // using API available since 3.1.3, can handle nulls in objectName and subobjectName + StringBuilder sb = new StringBuilder(); + sb.append("SELECT count(*)\n"); + sb.append(" FROM TABLE(ut_runner.get_suites_info(upper(?), upper(?)))\n"); + sb.append(" WHERE item_type IN ('UT_TEST', 'UT_SUITE')\n"); + sb.append(" AND (item_name = upper(?) or ? IS NULL)\n"); + final String sql = sb.toString(); + final Object[] binds = new Object[] {owner, objectName, subobjectName, subobjectName}; + final Integer found = jdbcTemplate.queryForObject(sql, Integer.class, binds); + return found > 0; + } else { + // using internal API (deprecated, not accessible in latest version) + StringBuilder sb = new StringBuilder(); + sb.append("SELECT count(\n"); + sb.append(" CASE\n"); + sb.append(" WHEN a.name = 'test'\n"); + sb.append(" AND (upper(a.subobject_name) = upper(?) OR ? IS NULL)\n"); + sb.append(" THEN\n"); + sb.append(" 1\n"); + sb.append(" ELSE\n"); + sb.append(" NULL\n"); + sb.append(" END\n"); + sb.append(" )\n"); + sb.append(" FROM TABLE("); + sb.append(getUtplsqlSchema()); + sb.append(".ut_annotation_manager.get_annotated_objects(upper(?), 'PACKAGE')) o\n"); + sb.append(" CROSS JOIN TABLE(o.annotations) a\n"); + sb.append(" WHERE (o.object_name = upper(?) OR ? IS NULL)\n"); + sb.append(" AND a.name IN ('test', 'suite')\n"); + sb.append("HAVING count(\n"); + sb.append(" CASE\n"); + sb.append(" WHEN a.name = 'suite' THEN\n"); + sb.append(" 1\n"); + sb.append(" ELSE\n"); + sb.append(" NULL\n"); + sb.append(" END\n"); + sb.append(" ) > 0"); + final String sql = sb.toString(); + final Object[] binds = new Object[] {subobjectName, subobjectName, owner, objectName, objectName}; + final Integer found = jdbcTemplate.queryForObject(sql, Integer.class, binds); + return found > 0; + } + } catch (EmptyResultDataAccessException e) { + return false; + } + } + + public boolean containsUtplsqlTest(final String owner) { + if (normalizedUtPlsqlVersionNumber() >= org.utplsql.sqldev.dal.UtplsqlDao.FIRST_VERSION_WITH_HAS_SUITES_API) { + // use faster check function available since v3.1.3 (reliable in v3.1.8) + StringBuilder sb = new StringBuilder(); + sb.append("DECLARE\n"); + sb.append(" l_return VARCHAR2(1) := '0';\n"); + sb.append("BEGIN\n"); + sb.append(" IF ut_runner.has_suites(?) THEN\n"); + sb.append(" l_return := '1';\n"); + sb.append(" END IF;\n"); + sb.append(" ? := l_return;\n"); + sb.append("END;"); + final String sql = sb.toString(); + return jdbcTemplate.execute(sql, new CallableStatementCallback() { + @Override + public Boolean doInCallableStatement(final CallableStatement cs) + throws SQLException { + cs.setString(1, owner); + cs.registerOutParameter(2, Types.VARCHAR); + cs.execute(); + final String ret = cs.getString(2); + return "1".equals(ret); + } + }); + } else { + return containsUtplsqlTest(owner, null, null); + } + } + + public boolean containsUtplsqlTest(final String owner, final String objectName) { + if (normalizedUtPlsqlVersionNumber() >= org.utplsql.sqldev.dal.UtplsqlDao.FIRST_VERSION_WITH_HAS_SUITES_API) { + StringBuilder sb = new StringBuilder(); + sb.append("DECLARE\n"); + sb.append(" l_return VARCHAR2(1) := '0';\n"); + sb.append("BEGIN\n"); + sb.append(" IF ut_runner.is_suite(?, ?) THEN\n"); + sb.append(" l_return := '1';\n"); + sb.append(" END IF;\n"); + sb.append(" ? := l_return;\n"); + sb.append("END;"); + final String sql = sb.toString(); + return jdbcTemplate.execute(sql, new CallableStatementCallback() { + @Override + public Boolean doInCallableStatement(final CallableStatement cs) + throws SQLException { + cs.setString(1, owner); + cs.setString(2, objectName); + cs.registerOutParameter(3, Types.VARCHAR); + cs.execute(); + final String ret = cs.getString(3); + return "1".equals(ret); + } + }); + } else { + return containsUtplsqlTest(owner, objectName, null); + } + } + + /** + * Gets a list of utPLSQL annotations for a given PL/SQL package specification + * + * @param owner + * schema name, mandatory, case-insensitive + * @param objectName + * name of the package or package body, optional, case-insensitive + * @return list of Annotation with name 'suite' or 'test' + * @throws DataAccessException + * if a utPLSQL version less than 3.0.4 is installed or if there are + * other problems + */ + public List annotations(final String owner, final String objectName) { + StringBuilder sb = new StringBuilder(); + if (normalizedUtPlsqlVersionNumber() >= FIRST_VERSION_WITH_ANNOTATION_API) { + sb.append("SELECT object_owner,\n"); + sb.append(" object_name,\n"); + sb.append(" lower(substr(item_type, 4)) AS name,\n"); + sb.append(" item_name as subobject_name\n"); + sb.append(" FROM TABLE(ut_runner.get_suites_info(upper(?), upper(?)))"); + } else { + sb.append("SELECT o.object_owner,\n"); + sb.append(" o.object_name,\n"); + sb.append(" a.name,\n"); + sb.append(" a.text,\n"); + sb.append(" coalesce(upper(a.subobject_name), o.object_name) AS subobject_name\n"); + sb.append(" FROM TABLE("); + sb.append(getUtplsqlSchema()); + sb.append(".ut_annotation_manager.get_annotated_objects(upper(?), 'PACKAGE')) o\n"); + sb.append(" CROSS JOIN TABLE(o.annotations) a\n"); + sb.append(" WHERE o.object_name = upper(?)"); + } + final String sql = sb.toString(); + final BeanPropertyRowMapper rowMapper = new BeanPropertyRowMapper<>(Annotation.class); + final Object[] binds = new Object[] {owner, objectName}; + return jdbcTemplate.query(sql, rowMapper, binds); + } + + /** + * Gets a list of public units in the object type + * + * @param objectType + * expected object types are PACKAGE, TYPE, FUNCTION, PROCEDURE + * @param objectName + * name of the object + * @return list of the public units in the object type + * @throws DataAccessException + * if there is a problem + */ + public List units(final String objectType, final String objectName) { + if ("PACKAGE".equals(objectType) || "TYPE".equals(objectType)) { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT procedure_name\n"); + sb.append(" FROM user_procedures\n"); + sb.append(" WHERE object_type = ?\n"); + sb.append(" AND object_name = ?\n"); + sb.append(" AND procedure_name IS NOT NULL\n"); + sb.append(" GROUP BY procedure_name\n"); + sb.append(" ORDER BY min(subprogram_id)"); + final String sql = sb.toString(); + final Object[] binds = new Object[] {objectType, objectName}; + return jdbcTemplate.queryForList(sql, String.class, binds); + } else { + return Arrays.asList(objectName); + } + } + + /** + * Gets a list of oddgen's nodes as candidates to create utPLSQL test packages. + * Candidates are packages, types, functions and procedures in the current user. + * + * This functions must be called from an oddgen generator only, since the Node + * is not defined in the utPLSQL extension. + * + * @param objectType + * expected object types are PACKAGE, TYPE, FUNCTION, PROCEDURE + * @return list of the oddgen nodes for the requested object type + * @throws DataAccessException + * if there is a problem + */ + public List testables(final String objectType) { + StringBuilder sb = new StringBuilder(); + if ("PACKAGE".equals(objectType)) { + if (normalizedUtPlsqlVersionNumber() >= FIRST_VERSION_WITH_ANNOTATION_API) { + // using API available since 3.1.3 + sb.append("SELECT DISTINCT\n"); + sb.append(" object_type || '.' || object_name AS id,\n"); + sb.append(" object_type AS parent_id,\n"); + sb.append(" 1 AS leaf,\n"); + sb.append(" 1 AS generatable,\n"); + sb.append(" 1 AS multiselectable\n"); + sb.append(" FROM user_procedures\n"); + sb.append(" WHERE object_type = ?\n"); + sb.append(" AND procedure_name IS NOT NULL\n"); + sb.append(" AND object_name NOT IN (\n"); + sb.append(" SELECT object_name\n"); + sb.append(" FROM TABLE(ut_runner.get_suites_info(USER))\n"); + sb.append(" WHERE item_type = 'UT_SUITE'\n"); + sb.append(" )"); + } else { + // using internal API (deprecated, not accessible in latest version) + sb.append("SELECT DISTINCT\n"); + sb.append(" object_type || '.' || object_name AS id,\n"); + sb.append(" object_type AS parent_id,\n"); + sb.append(" 1 AS leaf,\n"); + sb.append(" 1 AS generatable,\n"); + sb.append(" 1 AS multiselectable\n"); + sb.append(" FROM user_procedures\n"); + sb.append(" WHERE object_type = ?\n"); + sb.append(" AND procedure_name IS NOT NULL\n"); + sb.append(" AND object_name NOT IN (\n"); + sb.append(" SELECT object_name\n"); + sb.append(" FROM TABLE(\n"); + sb.append(getUtplsqlSchema()); + sb.append(".ut_annotation_manager.get_annotated_objects(USER, 'PACKAGE'))\n"); + sb.append(" )"); + } + } else if ("TYPE".equals(objectType)) { + sb.append("SELECT DISTINCT\n"); + sb.append(" object_type || '.' || object_name AS id,\n"); + sb.append(" object_type AS parent_id,\n"); + sb.append(" 1 AS leaf,\n"); + sb.append(" 1 AS generatable,\n"); + sb.append(" 1 AS multiselectable\n"); + sb.append(" FROM user_procedures\n"); + sb.append(" WHERE object_type = ?\n"); + sb.append(" AND procedure_name IS NOT NULL"); + } else { + sb.append("SELECT object_type || '.' || object_name AS id,\n"); + sb.append(" object_type AS parent_id,\n"); + sb.append(" 1 AS leaf,\n"); + sb.append(" 1 AS generatable,\n"); + sb.append(" 1 AS multiselectable\n"); + sb.append(" FROM user_objects\n"); + sb.append(" WHERE object_type = ?\n"); + sb.append(" AND generated = 'N'"); + } + final String sql = sb.toString(); + final Object[] binds = new Object[] {objectType}; + BeanPropertyRowMapper rowMapper = new BeanPropertyRowMapper<>(Node.class); + return jdbcTemplate.query(sql, rowMapper, binds); + } + + /** + * Gets a list of oddgen's nodes as candidates to run utPLSQL tests. + * + * This functions must be called from an oddgen generator only, since the Node + * is not defined in the utPLSQL extension. + * + * @return list of oddgen nodes (complete hierarchy loaded eagerly) + * @throws DataAccessException + * if there is a problem + */ + public List runnables() { + StringBuilder sb = new StringBuilder(); + if (normalizedUtPlsqlVersionNumber() >= FIRST_VERSION_WITH_ANNOTATION_API) { + // using API available since 3.1.3 + sb.append("WITH\n"); + sb.append(" test AS (\n"); + sb.append(" SELECT object_owner,\n"); + sb.append(" object_name,\n"); + sb.append(" path AS suitepath,\n"); + sb.append(" count(\n"); + sb.append(" CASE\n"); + sb.append(" WHEN item_type = 'UT_TEST' THEN\n"); + sb.append(" 1\n"); + sb.append(" ELSE\n"); + sb.append(" NULL\n"); + sb.append(" END\n"); + sb.append(" ) over (partition by object_owner, object_name) AS test_count,\n"); + sb.append(" item_type,\n"); + sb.append(" item_name,\n"); + sb.append(" item_description\n"); + sb.append(" FROM TABLE(ut_runner.get_suites_info(user))\n"); + sb.append(" ),\n"); + sb.append(" suite_tree AS (\n"); + sb.append(" SELECT null AS parent_id,\n"); + sb.append(" 'SUITE' AS id,\n"); + sb.append(" 'All Suites' AS name,\n"); + sb.append(" 'All utPLSQL test suites' AS description,\n"); + sb.append(" 'PACKAGE_FOLDER_ICON' AS iconName,\n"); + sb.append(" 'No' AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable,\n"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM dual\n"); + sb.append(" UNION ALL\n"); + sb.append(" SELECT DISTINCT\n"); + sb.append(" 'SUITE' AS parent_id,\n"); + sb.append(" object_owner || '.' || object_name AS id,\n"); + sb.append(" object_name AS name,\n"); + sb.append(" null AS description,\n"); + sb.append(" 'PACKAGE_ICON' AS iconName,\n"); + sb.append(" 'No' AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable,\n"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM test\n"); + sb.append(" WHERE item_type IN ('UT_TEST', 'UT_SUITE')\n"); + sb.append(" UNION ALL\n"); + sb.append(" SELECT object_owner || '.' || object_name AS parent_id,\n"); + sb.append(" object_owner || '.' || object_name || '.' || item_name AS id,\n"); + sb.append(" item_name AS name,\n"); + sb.append(" item_description AS description,\n"); + sb.append(" 'PROCEDURE_ICON' AS iconName,\n"); + sb.append(" 'Yes' AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable,\n"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM test\n"); + sb.append(" WHERE item_type = 'UT_TEST'\n"); + sb.append(" ),\n"); + sb.append(" suitepath_tree AS (\n"); + sb.append(" SELECT NULL AS parent_id,\n"); + sb.append(" 'SUITEPATH' AS id,\n"); + sb.append(" 'All Suitepaths' AS name\n,"); + sb.append(" 'All utPLSQL test suitepathes' AS description,\n"); + sb.append(" 'FOLDER_ICON' AS iconName,\n"); + sb.append(" 'No' AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable,\n"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM dual\n"); + sb.append(" UNION ALL\n"); + sb.append(" SELECT CASE\n"); + sb.append(" WHEN regexp_replace(suitepath,'\\.?\\w+$','') IS NULL THEN\n"); + sb.append(" 'SUITEPATH'\n"); + sb.append(" ELSE\n"); + sb.append(" object_owner || ':' || regexp_replace(suitepath,'\\.?\\w+$','')\n"); + sb.append(" END AS parent_id,\n"); + sb.append(" object_owner || ':' || suitepath AS id,\n"); + sb.append(" item_name AS name,\n"); + sb.append(" item_description AS description,\n"); + sb.append(" CASE\n"); + sb.append(" WHEN item_type = 'UT_SUITE' AND test_count > 0 THEN\n"); + sb.append(" 'PACKAGE_ICON'\n"); + sb.append(" WHEN item_type = 'UT_TEST' THEN\n"); + sb.append(" 'PROCEDURE_ICON'\n"); + sb.append(" ELSE\n"); + sb.append(" 'FOLDER_ICON'\n"); + sb.append(" END AS iconName,\n"); + sb.append(" CASE item_type\n"); + sb.append(" WHEN 'UT_TEST' THEN\n"); + sb.append(" 'Yes'\n"); + sb.append(" ELSE\n"); + sb.append(" 'No'\n"); + sb.append(" END AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable,\n"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM test\n"); + sb.append(" ),\n"); + sb.append(" tree AS (\n"); + sb.append(" SELECT parent_id, id, name, description, iconName, leaf, generatable, multiselectable, relevant\n"); + sb.append(" FROM suite_tree\n"); + sb.append(" UNION ALL\n"); + sb.append(" SELECT parent_id, id, name, description, iconName, leaf, generatable, multiselectable, relevant\n"); + sb.append(" FROM suitepath_tree\n"); + sb.append(" )\n"); + sb.append("SELECT parent_id, id, initcap(name) AS name, description, iconName, leaf, generatable, multiselectable, relevant\n"); + sb.append(" FROM tree"); + } else { + // using internal API (deprecated, not accessible in latest version) + sb.append("WITH\n"); + sb.append(" base AS (\n"); + sb.append(" SELECT rownum AS an_id,\n"); + sb.append(" o.object_owner,\n"); + sb.append(" o.object_type,\n"); + sb.append(" o.object_name,\n"); + sb.append(" lower(a.name) AS name,\n"); + sb.append(" a.text,\n"); + sb.append(" a.subobject_name\n"); + sb.append(" FROM table("); + sb.append(getUtplsqlSchema()); + sb.append(".ut_annotation_manager.get_annotated_objects(user, 'PACKAGE')) o\n"); + sb.append(" CROSS JOIN table(o.annotations) a\n"); + sb.append(" WHERE lower(a.name) in ('suite', 'suitepath', 'endcontext', 'test')\n"); + sb.append(" OR lower(a.name) = 'context' AND regexp_like(text, '(\\w+)(\\.\\w+)*')\n"); + sb.append(" ),\n"); + sb.append(" suite AS (\n"); + sb.append(" SELECT object_owner, object_type, object_name, text AS suite_description\n"); + sb.append(" FROM base\n"); + sb.append(" WHERE name = 'suite'\n"); + sb.append(" ),\n"); + sb.append(" suitepath as (\n"); + sb.append(" SELECT object_owner, object_type, object_name, lower(text) AS suitepath\n"); + sb.append(" FROM base\n"); + sb.append(" WHERE name = 'suitepath'"); + sb.append(" ),\n"); + sb.append(" context_base AS (\n"); + sb.append(" SELECT an_id,\n"); + sb.append(" lead(an_id) over (partition by object_owner, object_type, object_name order by an_id) AS an_id_end,\n"); + sb.append(" object_owner,\n"); + sb.append(" object_type,\n"); + sb.append(" object_name,\n"); + sb.append(" name,\n"); + sb.append(" lead(name) over (partition by object_owner, object_type, object_name order by an_id) AS name_end,\n"); + sb.append(" text AS context\n"); + sb.append(" FROM base\n"); + sb.append(" WHERE name IN ('context', 'endcontext')\n"); + sb.append(" ),\n"); + sb.append(" context AS (\n"); + sb.append(" SELECT an_id, an_id_end, object_owner, object_type, object_name, context\n"); + sb.append(" FROM context_base\n"); + sb.append(" WHERE name = 'context'\n"); + sb.append(" AND name_end = 'endcontext'\n"); + sb.append(" ),\n"); + sb.append(" test AS (\n"); + sb.append(" SELECT b.an_id,\n"); + sb.append(" b.object_owner,\n"); + sb.append(" b.object_type,\n"); + sb.append(" b.object_name,\n"); + sb.append(" p.suitepath,\n"); + sb.append(" c.context,\n"); + sb.append(" b.subobject_name,\n"); + sb.append(" b.text AS test_description\n"); + sb.append(" FROM base b\n"); + sb.append(" LEFT JOIN suitepath p\n"); + sb.append(" ON p.object_owner = b.object_owner\n"); + sb.append(" AND p.object_type = b.object_type\n"); + sb.append(" AND p.object_name = b.object_name\n"); + sb.append(" LEFT JOIN context c\n"); + sb.append(" ON c.object_owner = b.object_owner\n"); + sb.append(" AND c.object_type = b.object_type\n"); + sb.append(" AND c.object_name = b.object_name\n"); + sb.append(" AND b.an_id BETWEEN c.an_id AND c.an_id_end\n"); + sb.append(" WHERE name = 'test'\n"); + sb.append(" AND (b.object_owner, b.object_type, b.object_name) IN (\n"); + sb.append(" SELECT object_owner, object_type, object_name\n"); + sb.append(" FROM suite\n"); + sb.append(" )\n"); + sb.append(" ),\n"); + sb.append(" suite_tree AS (\n"); + sb.append(" SELECT null AS parent_id,\n"); + sb.append(" 'SUITE' AS id,\n"); + sb.append(" 'All Suites' AS name,\n"); + sb.append(" 'All utPLSQL test suites' AS description,\n"); + sb.append(" 'PACKAGE_FOLDER_ICON' AS iconName,\n"); + sb.append(" 'No' AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable,\n"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM dual\n"); + sb.append(" UNION ALL\n"); + sb.append(" SELECT DISTINCT\n"); + sb.append(" 'SUITE' AS parent_id,\n"); + sb.append(" 'object_owner || '.' || object_name AS id,\n"); + sb.append(" object_name AS name,\n"); + sb.append(" NULL AS description,\n"); + sb.append(" 'PACKAGE_ICON' AS iconName,\n"); + sb.append(" 'No' AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable,\n"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM test\n"); + sb.append(" UNION ALL\n"); + sb.append(" SELECT object_owner || '.' || object_name AS parent_id,\n"); + sb.append(" object_owner || '.' || object_name || '.' || upper(subobject_name) AS id,\n"); + sb.append(" subobject_name AS name,\n"); + sb.append(" NULL AS description,\n"); + sb.append(" 'PROCEDURE_ICON' AS iconName,\n"); + sb.append(" 'Yes' AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable,\n"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM test\n"); + sb.append(" ),\n"); + sb.append(" suitepath_base AS (\n"); + sb.append(" SELECT DISTINCT\n"); + sb.append(" suitepath\n"); + sb.append(" FROM suitepath\n"); + sb.append(" ),\n"); + sb.append(" gen AS (\n"); + sb.append(" SELECT rownum AS pos\n"); + sb.append(" FROM xmltable('1 to 100')\n"); + sb.append(" ),\n"); + sb.append(" suitepath_part AS (\n"); + sb.append(" SELECT DISTINCT\n"); + sb.append(" lower(substr(suitepath, 1, instr(suitepath || '.', '.', 1, g.pos) -1)) AS suitepath\n"); + sb.append(" FROM suitepath_base b\n"); + sb.append(" JOIN gen g\n"); + sb.append(" ON g.pos <= regexp_count(suitepath, '\\w+')\n"); + sb.append(" ),\n"); + sb.append(" suitepath_tree AS (\n"); + sb.append(" SELECT NULL AS parent_id,\n"); + sb.append(" 'SUITEPATH' AS id,\n"); + sb.append(" 'All Suitepaths' AS name,\n"); + sb.append(" 'All utPLSQL test suitepathes' AS description,\n"); + sb.append(" 'FOLDER_ICON' AS iconName,\n"); + sb.append(" 'No' AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable,\n"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM dual\n"); + sb.append(" UNION ALL\n"); + sb.append(" SELECT CASE\n"); + sb.append(" WHEN regexp_replace(suitepath,'\\.?\\w+$','') IS NULL THEN\n"); + sb.append(" 'SUITEPATH'\n"); + sb.append(" ELSE\n"); + sb.append(" USER || ':' || regexp_replace(suitepath,'\\.?\\w+$','')"); + sb.append(" END AS parent_id,\n"); + sb.append(" USER || ':' || suitepath AS id,\n"); + sb.append(" regexp_substr(suitepath, '\\.?(\\w+$)', 1, 1, NULL, 1) AS name,\n"); + sb.append(" NULL AS description,\n"); + sb.append(" 'FOLDER_ICON' AS iconName,\n"); + sb.append(" 'No' AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable,\n"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM suitepath_part\n"); + sb.append(" UNION ALL\n"); + sb.append(" SELECT DISTINCT\n"); + sb.append(" object_owner || ':' || suitepath AS parent_id,\n"); + sb.append(" object_owner || ':' || suitepath || '.' || lower(object_name) AS id,\n"); + sb.append(" object_name AS name,\n"); + sb.append(" NULL AS description,\n"); + sb.append(" 'PACKAGE_ICON' AS iconName,\n"); + sb.append(" 'No' AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable,\n"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM test\n"); + sb.append(" WHERE suitepath IS NOT NULL\n"); + sb.append(" UNION ALL\n"); + sb.append(" SELECT DISTINCT\n"); + sb.append(" object_owner || ':' || suitepath || '.' || lower(object_name) AS parent_id,\n"); + sb.append(" object_owner || ':' || suitepath || '.' || lower(object_name) || '.' || context AS id,\n"); + sb.append(" context AS name,\n"); + sb.append(" NULL AS description,\n"); + sb.append(" 'FOLDER_ICON' AS iconName,\n"); + sb.append(" 'No' AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable,\n"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM test\n"); + sb.append(" WHERE suitepath IS NOT NULL\n"); + sb.append(" AND context IS NOT NULL\n"); + sb.append(" UNION ALL\n"); + sb.append(" SELECT object_owner || ':' || suitepath || '.' || lower(object_name) || CASE WHEN context IS NOT NULL THEN '.' || context END AS parent_id,\n"); + sb.append(" object_owner || ':' || suitepath || '.' || lower(object_name) || CASE WHEN context IS NOT NULL THEN '.' || context END || '.' || lower(subobject_name) AS id,\n"); + sb.append(" subobject_name AS name,\n"); + sb.append(" NULL AS description,\n"); + sb.append(" 'PROCEDURE_ICON' AS iconName,\n"); + sb.append(" 'Yes' AS leaf,\n"); + sb.append(" 'Yes' AS generatable,\n"); + sb.append(" 'Yes' AS multiselectable\n,"); + sb.append(" 'Yes' AS relevant\n"); + sb.append(" FROM test\n"); + sb.append(" WHERE suitepath IS NOT NULL\n"); + sb.append(" ),\n"); + sb.append(" tree AS (\n"); + sb.append(" SELECT parent_id, id, name, description, iconName, leaf, generatable, multiselectable, relevant\n"); + sb.append(" FROM suite_tree\n"); + sb.append(" UNION ALL\n"); + sb.append(" SELECT parent_id, id, name, description, iconName, leaf, generatable, multiselectable, relevant\n"); + sb.append(" FROM suitepath_tree\n"); + sb.append(" )\n"); + sb.append("SELECT parent_id, id, initcap(name) AS name, description, iconName, leaf, generatable, multiselectable, relevant\n"); + sb.append(" FROM tree"); + } + BeanPropertyRowMapper rowMapper = new BeanPropertyRowMapper<>(Node.class); + final String sql = sb.toString(); + return jdbcTemplate.query(sql, rowMapper); + } + + /** + * enable DBMS_OUTPUT + * + * @throws DataAccessException + * if there is a problem + */ + public void enableDbmsOutput() { + // equivalent to "set serveroutput on size unlimited" + StringBuilder sb = new StringBuilder(); + sb.append("BEGIN"); + sb.append(" sys.dbms_output.enable(NULL);\n"); + sb.append("END;"); + jdbcTemplate.update(sb.toString()); + } + + /** + * disable DBMS_OUTPUT + * + * @throws DataAccessException + * if there is a problem + */ + public void disableDbmsOutput() { + StringBuilder sb = new StringBuilder(); + sb.append("BEGIN\n"); + sb.append(" sys.dbms_output.disable;\n"); + sb.append("END;"); + jdbcTemplate.update(sb.toString()); + } + + /** + * return the content of DBMS_OUTPUT as String + * + * @throws DataAccessException + * if there is a problem + */ + public String getDbmsOutput() { + return getDbmsOutput(1000); + } + + /** + * return the content of DBMS_OUTPUT as String + * + * @param bufferSize + * maximum number of rows to be read from the DBMS_OUTPUT buffer in + * one network round trip + * @return content of DBMS_OUTPUT as String + * @throws DataAccessException + * if there is a problem + */ + public String getDbmsOutput(final int bufferSize) { + final StringBuilder resultSb = new StringBuilder(); + StringBuilder sb = new StringBuilder(); + sb.append("BEGIN"); + sb.append(" sys.dbms_output.get_lines(?, ?);\n"); + sb.append("END;"); + final String sql = sb.toString(); + OutputLines ret = null; + do { + ret = jdbcTemplate.execute(sql, new CallableStatementCallback() { + @Override + public OutputLines doInCallableStatement(final CallableStatement cs) throws SQLException { + cs.registerOutParameter(1, Types.ARRAY, "DBMSOUTPUT_LINESARRAY"); + cs.registerOutParameter(2, Types.INTEGER); + cs.setInt(2, bufferSize); + cs.execute(); + final OutputLines out = new OutputLines(); + Object array = cs.getArray(1).getArray(); + out.setLines((String[]) array); + out.setNumlines(cs.getInt(2)); + return out; + } + }); + for (int i = 0; i < ret.getNumlines(); i++) { + final String line = ret.getLines()[i]; + if (line != null) { + resultSb.append(ret.getLines()[i]); + } + resultSb.append(System.lineSeparator()); + } + } while (ret.getNumlines() > 0); + return resultSb.toString(); + } + + /** + * gets the HTML code coverage report as String + * + * @param pathList + * utPLSQL path list + * @param schemaList + * list of schemas under tests. Current schema, if empty + * @param includeObjectList + * list of objects to be included for coverage analysis. All, if + * empty + * @param excludeObjectList + * list of objects to be excluded from coverage analysis. None, if + * empty + * @return HTML code coverage report in HTML format + * @throws DataAccessException + * if there is a problem + */ + public String htmlCodeCoverage(final List pathList, final List schemaList, + final List includeObjectList, final List excludeObjectList) { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT column_value\n"); + sb.append(" FROM table(\n"); + sb.append(" ut.run(\n"); + sb.append(" a_paths => ut_varchar2_list(\n"); + sb.append(StringTools.getCSV(pathList, 16)); + sb.append(" ),\n"); + if (!schemaList.isEmpty()) { + sb.append(" a_coverage_schemes => ut_varchar2_list(\n"); + sb.append(StringTools.getCSV(schemaList, 16)); + sb.append(" ),\n"); + } + if (!includeObjectList.isEmpty()) { + sb.append(" a_include_objects => ut_varchar2_list(\n"); + sb.append(StringTools.getCSV(includeObjectList, 16)); + sb.append(" ),\n"); + } + if (!excludeObjectList.isEmpty()) { + sb.append(" a_exclude_objects => ut_varchar2_list(\n"); + sb.append(StringTools.getCSV(excludeObjectList, 16)); + sb.append(" ),\n"); + } + sb.append(" a_reporter => ut_coverage_html_reporter()\n"); + sb.append(" )\n"); + sb.append(" )"); + final String sql = sb.toString(); + final List lines = jdbcTemplate.queryForList(sql, String.class); + final StringBuilder resultSb = new StringBuilder(); + for (String line : lines) { + if (line != null) { + resultSb.append(line); + resultSb.append("\n"); + } + } + return resultSb.toString(); + } + + /** + * gets dependencies of a given object. + * + * The result can be used as input for the includeObjectList in htmlCodeCoverage + * The scope is reduced to non-oracle maintained schemas. + * + * Oracle introduced the column ORACLE_MAINTAINED in 12.1. To simplify the query + * and compatibility the result of the following query is included + * + * SELECT '''' || listagg(username, ''', ''') || '''' AS oracle_maintained_users + * FROM dba_users WHERE oracle_maintained = 'Y' ORDER BY username; + * + * The result may include test packages + * + * @param name + * test package name + * @return list of dependencies in the current schema + */ + public List includes(final String owner, final String name) { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT referenced_owner || '.' || referenced_name AS dep_name\n"); + sb.append(" FROM "); + sb.append(getDbaView("dependencies\n")); + sb.append(" WHERE owner = upper(?)\n"); + sb.append(" AND name = upper(?)\n"); + sb.append(" AND referenced_owner NOT IN (\n"); + sb.append(" 'SYS', 'SYSTEM', 'XS$NULL', 'OJVMSYS', 'LBACSYS', 'OUTLN', 'SYS$UMF',\n"); + sb.append(" 'DBSNMP', 'APPQOSSYS', 'DBSFWUSER', 'GGSYS', 'ANONYMOUS', 'CTXSYS',\n"); + sb.append(" 'SI_INFORMTN_SCHEMA', 'DVF', 'DVSYS', 'GSMADMIN_INTERNAL', 'ORDPLUGINS',\n"); + sb.append(" 'MDSYS', 'OLAPSYS', 'ORDDATA', 'XDB', 'WMSYS', 'ORDSYS', 'GSMCATUSER',\n"); + sb.append(" 'MDDATA', 'REMOTE_SCHEDULER_AGENT', 'SYSBACKUP', 'GSMUSER', 'APEX_PUBLIC_USER',\n"); + sb.append(" 'SYSRAC', 'AUDSYS', 'DIP', 'SYSKM', 'ORACLE_OCM', 'APEX_INSTANCE_ADMIN_USER',\n"); + sb.append(" 'SYSDG', 'FLOWS_FILES', 'ORDS_METADATA', 'ORDS_PUBLIC_USER'\n"); + sb.append(" )\n"); + sb.append(" AND referenced_owner NOT LIKE 'APEX\\_______'"); + sb.append(" AND referenced_type IN ('PACKAGE', 'TYPE', 'PROCEDURE', 'FUNCTION', 'TRIGGER')"); + final String sql = sb.toString(); + final Object[] binds = new Object[] {owner, name}; + return jdbcTemplate.queryForList(sql, String.class, binds); + } + + /** + * gets source of an object from the database via DBMS_METADATA + * + * @param owner + * owner of the object (schema) + * @param objectType + * expected object types are PACKAGE, PACKAGE BODY + * @param objectName + * name of the object + * @return the source code of the object + * @throws DataAccessException + * if there is a problem + */ + public String getSource(final String owner, final String objectType, final String objectName) { + String fixedObjectType; + if ("PACKAGE".equals(objectType)) { + fixedObjectType = "PACKAGE_SPEC"; + } else if ("PACKAGE BODY".equals(objectType)) { + fixedObjectType = "PACKAGE_BODY"; + } else { + fixedObjectType = objectType; + } + StringBuilder sb = new StringBuilder(); + sb.append("BEGIN\n"); + sb.append(" ? := sys.dbms_metadata.get_ddl(\n"); + sb.append(" schema => ?,\n"); + sb.append(" object_type => ?,\n"); + sb.append(" name => ?\n"); + sb.append(" );\n"); + sb.append("END;"); + final String sql = sb.toString(); + return jdbcTemplate.execute(sql, new CallableStatementCallback() { + @Override + public String doInCallableStatement(final CallableStatement cs) throws SQLException { + cs.registerOutParameter(1, Types.CLOB); + cs.setString(2, owner); + cs.setString(3, fixedObjectType); + cs.setString(4, objectName); + cs.execute(); + return cs.getString(1); + } + }); + } + + /** + * gets the object type of a database object + * + * The object types "PACKAGE BODY", "TYPE BODY" have higher priority. "PACKAGE" + * OR "TYPE" will be returned only when no body exists. + * + * @param owner + * owner of the object (schema) + * @param objectName + * name of the object + * @return the object type, e.g. PACKAGE BODY, TYPE BODY, PROCEDURE, FUNCTION + */ + public String getObjectType(final String owner, final String objectName) { + StringBuilder sb = new StringBuilder(); + sb.append("SELECT object_type\n"); + sb.append(" FROM (\n"); + sb.append(" SELECT object_type\n"); + sb.append(" FROM "); + sb.append(getDbaView("objects\n")); + sb.append(" WHERE owner = ?\n"); + sb.append(" AND object_name = ?\n"); + sb.append(" ORDER BY decode(object_type, 'PACKAGE', 10, 'TYPE', 10, 'SYNONYM', 20, 1)\n"); + sb.append(" )\n"); + sb.append(" WHERE rownum = 1"); + final String sql = sb.toString(); + final Object[] binds = new Object[] {owner, objectName}; + return jdbcTemplate.queryForObject(sql, binds, String.class); + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/dal/UtplsqlDao.xtend b/sqldev/src/main/java/org/utplsql/sqldev/dal/UtplsqlDao.xtend deleted file mode 100644 index 7070a25f..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/dal/UtplsqlDao.xtend +++ /dev/null @@ -1,1020 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.utplsql.sqldev.dal - -import java.sql.CallableStatement -import java.sql.Connection -import java.sql.SQLException -import java.sql.Types -import java.util.List -import java.util.regex.Pattern -import org.oddgen.sqldev.generators.model.Node -import org.springframework.dao.DataAccessException -import org.springframework.dao.EmptyResultDataAccessException -import org.springframework.jdbc.core.BeanPropertyRowMapper -import org.springframework.jdbc.core.CallableStatementCallback -import org.springframework.jdbc.core.JdbcTemplate -import org.springframework.jdbc.datasource.SingleConnectionDataSource -import org.utplsql.sqldev.model.ut.Annotation -import org.utplsql.sqldev.model.ut.OutputLines - -class UtplsqlDao { - public static val UTPLSQL_PACKAGE_NAME = "UT" - public static val NOT_INSTALLED = 0000000 - public static val FIRST_VERSION_WITH_INTERNAL_ANNOTATION_API = 3000004 - public static val FIRST_VERSION_WITH_ANNOTATION_API = 3001003 - public static val FIRST_VERSION_WITHOUT_INTERNAL_API = 3001008 - public static val FIRST_VERSION_WITH_HAS_SUITES_API = 3001008 - var Connection conn - var JdbcTemplate jdbcTemplate - // cache fields - Boolean cachedDbaViewAccessible - String cachedUtplsqlSchema - String cachedUtPlsqlVersion - - new(Connection connection) { - conn = connection - jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(conn, true)) - } - - /** - * used for testing purposes only - */ - def setUtPlsqlVersion(String utPlsqlVersion) { - cachedUtPlsqlVersion = utPlsqlVersion - } - - /** - * returns a normalized utPLSQL version in format 9.9.9 - */ - def String normalizedUtPlsqlVersion() { - val version = getUtPlsqlVersion() - if (version !== null) { - val p = Pattern.compile("(\\d+\\.\\d+\\.\\d+)") - val m = p.matcher(version) - if (m.find) { - return m.group(0) - } - } - return "0.0.0" - } - - /** - * get version as number, e.g. 3001004 - */ - def int normalizedUtPlsqlVersionNumber() { - val p = Pattern.compile("(\\d+)") - val version = normalizedUtPlsqlVersion() - val m = p.matcher(version) - m.find - val major = m.group - m.find - val minor = m.group - m.find - val bugfix = m.group - val versionNumber = Integer.valueOf(major)*1000000 + Integer.valueOf(minor)*1000 + Integer.valueOf(bugfix) - return versionNumber - } - - /** - * gets version of installed utPLSQL - */ - def String getUtPlsqlVersion() { - if (cachedUtPlsqlVersion === null) { - val sql = ''' - BEGIN - ? := ut.version; - END; - ''' - try { - cachedUtPlsqlVersion = jdbcTemplate.execute(sql, new CallableStatementCallback() { - override String doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException { - cs.registerOutParameter(1, Types.VARCHAR); - cs.execute - val version = cs.getString(1) - return version - } - }) - } catch (SQLException e) { - // ignore error - } catch (DataAccessException e) { - // ignore error - } - } - return cachedUtPlsqlVersion - } - - def boolean isDbaViewAccessible() { - if (cachedDbaViewAccessible === null) { - try { - val sql = ''' - SELECT 1 AS dummy - FROM dba_objects - WHERE 1=2 - UNION ALL - SELECT 1 - FROM dba_synonyms - WHERE 1=2 - UNION ALL - SELECT 1 - FROM dba_dependencies - WHERE 1=2 - ''' - jdbcTemplate.execute(sql) - cachedDbaViewAccessible = true - } catch (DataAccessException e) { - cachedDbaViewAccessible = false - } - } - return cachedDbaViewAccessible.booleanValue - } - - /** - * Gets the schema name of the utPLSQL installation. - * - * @return utPLSQL schema or null if no utPLSQL is not installed - * @throws DataAccessException if there is a problem - */ - def String getUtplsqlSchema() { - if (cachedUtplsqlSchema === null) { - val sql = ''' - SELECT table_owner - FROM «IF dbaViewAccessible»dba«ELSE»all«ENDIF»_synonyms - WHERE owner = 'PUBLIC' - AND synonym_name = '«UTPLSQL_PACKAGE_NAME»' - AND table_name = '«UTPLSQL_PACKAGE_NAME»' - ''' - try { - val schema = jdbcTemplate.queryForObject(sql, String) - cachedUtplsqlSchema = schema - } catch (EmptyResultDataAccessException e) { - cachedUtplsqlSchema = null - } - } - return cachedUtplsqlSchema - } - - /** - * Checks if the package ut_annotation_manager is installed. - * This package has been introduced with utPLSQL 3.0.4. - * This version is a prerequisite to identify - * utPLSQL unit test procedures. - * - * @return true if ut_annotation_manager package has been found - * @throws DataAccessException if there is a problem - */ - def boolean isUtAnnotationManagerInstalled() { - return normalizedUtPlsqlVersionNumber >= FIRST_VERSION_WITH_INTERNAL_ANNOTATION_API - } - - /** - * Checks if utPLSQL tests exist - * - * @param owner schema name, mandatory, case-insensitive - * @param objectName name of the package or package body, optional, case-insensitive - * @param subobjectName name of the procedure, optional, case-insensitive - * @return true if at least one test has been found - * @throws DataAccessException if a utPLSQL version less than 3.0.4 is installed or if there are other problems - */ - def boolean containsUtplsqlTest(String owner, String objectName, String subobjectName) { - try { - if (normalizedUtPlsqlVersionNumber >= org.utplsql.sqldev.dal.UtplsqlDao.FIRST_VERSION_WITH_HAS_SUITES_API && objectName !== null && subobjectName !== null) { - // use faster check function available since v3.1.3 (reliable in v3.1.8) - val sql = ''' - DECLARE - l_return VARCHAR2(1) := '0'; - BEGIN - IF ut_runner.is_test(?, ?, ?) THEN - l_return := '1'; - END IF; - ? := l_return; - END; - ''' - val ret = jdbcTemplate.execute(sql, new CallableStatementCallback() { - override Boolean doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException { - cs.setString(1, owner) - cs.setString(2, objectName) - cs.setString(3, subobjectName) - cs.registerOutParameter(4, Types.VARCHAR); - cs.execute - val ret = cs.getString(4) - return ret == "1" - } - }) - return ret - } else if (normalizedUtPlsqlVersionNumber >= FIRST_VERSION_WITH_ANNOTATION_API) { - // using API available since 3.1.3, can handle nulls in objectName and subobjectName - val sql = ''' - SELECT count(*) - FROM TABLE(ut_runner.get_suites_info(upper(?), upper(?))) - WHERE item_type IN ('UT_TEST', 'UT_SUITE') - AND (item_name = upper(?) or ? IS NULL) - ''' - val found = jdbcTemplate.queryForObject(sql, Integer, #[owner, objectName, subobjectName, subobjectName]) - return found > 0 - } else { - // using internal API (deprecated) - val sql = ''' - SELECT count( - CASE - WHEN a.name = 'test' - AND (upper(a.subobject_name) = upper(?) OR ? IS NULL) - THEN - 1 - ELSE - NULL - END - ) - FROM TABLE(«utplsqlSchema».ut_annotation_manager.get_annotated_objects(upper(?), 'PACKAGE')) o - CROSS JOIN TABLE(o.annotations) a - WHERE (o.object_name = upper(?) OR ? IS NULL) - AND a.name IN ('test', 'suite') - HAVING count( - CASE - WHEN a.name = 'suite' THEN - 1 - ELSE - NULL - END - ) > 0 - ''' - val found = jdbcTemplate.queryForObject(sql, Integer, #[subobjectName, subobjectName, owner, objectName, objectName]) - return found > 0 - } - } catch (EmptyResultDataAccessException e) { - return false - } - } - - def boolean containsUtplsqlTest(String owner) { - if (normalizedUtPlsqlVersionNumber >= org.utplsql.sqldev.dal.UtplsqlDao.FIRST_VERSION_WITH_HAS_SUITES_API) { - // use faster check function available since v3.1.3 (reliable in v3.1.8) - val sql = ''' - DECLARE - l_return VARCHAR2(1) := '0'; - BEGIN - IF ut_runner.has_suites(?) THEN - l_return := '1'; - END IF; - ? := l_return; - END; - ''' - val ret = jdbcTemplate.execute(sql, new CallableStatementCallback() { - override Boolean doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException { - cs.setString(1, owner) - cs.registerOutParameter(2, Types.VARCHAR); - cs.execute - val ret = cs.getString(2) - return ret == "1" - } - }) - return ret - } else { - return containsUtplsqlTest(owner, null, null) - } - } - - def boolean containsUtplsqlTest(String owner, String objectName) { - if (normalizedUtPlsqlVersionNumber >= org.utplsql.sqldev.dal.UtplsqlDao.FIRST_VERSION_WITH_HAS_SUITES_API) { - // use faster check function available since v3.1.3 (reliable in v3.1.8) - val sql = ''' - DECLARE - l_return VARCHAR2(1) := '0'; - BEGIN - IF ut_runner.is_suite(?, ?) THEN - l_return := '1'; - END IF; - ? := l_return; - END; - ''' - val ret = jdbcTemplate.execute(sql, new CallableStatementCallback() { - override Boolean doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException { - cs.setString(1, owner) - cs.setString(2, objectName) - cs.registerOutParameter(3, Types.VARCHAR); - cs.execute - val ret = cs.getString(3) - return ret == "1" - } - }) - return ret - } else { - return containsUtplsqlTest(owner, objectName, null) - } - } - - /** - * Gets a list of utPLSQL annotations for a given PL/SQL package specification - * - * @param owner schema name, mandatory, case-insensitive - * @param objectName name of the package or package body, optional, case-insensitive - * @return list of Annotation with name 'suite' or 'test' - * @throws DataAccessException if a utPLSQL version less than 3.0.4 is installed or if there are other problems - */ - def List annotations(String owner, String objectName) { - var String sql - if (normalizedUtPlsqlVersionNumber >= FIRST_VERSION_WITH_ANNOTATION_API) { - // using API available since 3.1.3 - sql = ''' - SELECT object_owner, - object_name, - lower(substr(item_type, 4)) AS name, - item_name as subobject_name - FROM TABLE(ut_runner.get_suites_info(upper(?), upper(?))) - ''' - - } else { - // using internal API (deprecated) - sql = ''' - SELECT o.object_owner, - o.object_name, - a.name, - a.text, - coalesce(upper(a.subobject_name), o.object_name) AS subobject_name - FROM TABLE(«utplsqlSchema».ut_annotation_manager.get_annotated_objects(upper(?), 'PACKAGE')) o - CROSS JOIN TABLE(o.annotations) a - WHERE o.object_name = upper(?) - ''' - } - val result = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Annotation), #[owner, objectName]) - return result - } - - /** - * Gets a list of public units in the object type - * - * @param objectType expected object types are PACKAGE, TYPE, FUNCTION, PROCEDURE - * @param objectName name of the object - * @return list of the public units in the object type - * @throws DataAccessException if there is a problem - */ - def List units(String objectType, String objectName) { - if (objectType == "PACKAGE" || objectType == "TYPE") { - val sql = ''' - SELECT procedure_name - FROM user_procedures - WHERE object_type = ? - AND object_name = ? - AND procedure_name IS NOT NULL - GROUP BY procedure_name - ORDER BY min(subprogram_id) - ''' - val result = jdbcTemplate.queryForList(sql, String, #[objectType, objectName]) - return result - } else { - return #[objectName] - } - } - - /** - * Gets a list of oddgen's nodes as candidates to create utPLSQL test packages. - * Candidates are packages, types, functions and procedures in the current user. - * - * This functions must be called from an oddgen generator only, since the Node is not - * defined in the utPLSQL extension. - * - * @param objectType expected object types are PACKAGE, TYPE, FUNCTION, PROCEDURE - * @return list of the oddgen nodes for the requested object type - * @throws DataAccessException if there is a problem - */ - def List testables(String objectType) { - var String sql; - if (objectType == "PACKAGE") { - if (normalizedUtPlsqlVersionNumber >= FIRST_VERSION_WITH_ANNOTATION_API) { - // using API available since 3.1.3 - sql = ''' - SELECT DISTINCT - object_type || '.' || object_name AS id, - object_type AS parent_id, - 1 AS leaf, - 1 AS generatable, - 1 AS multiselectable - FROM user_procedures - WHERE object_type = ? - AND procedure_name IS NOT NULL - AND object_name NOT IN ( - SELECT object_name - FROM TABLE(ut_runner.get_suites_info(USER)) - WHERE item_type = 'UT_SUITE' - ) - ''' - } else { - // using internal API (deprecated) - sql = ''' - SELECT DISTINCT - object_type || '.' || object_name AS id, - object_type AS parent_id, - 1 AS leaf, - 1 AS generatable, - 1 AS multiselectable - FROM user_procedures - WHERE object_type = ? - AND procedure_name IS NOT NULL - AND object_name NOT IN ( - SELECT object_name - FROM TABLE(«utplsqlSchema».ut_annotation_manager.get_annotated_objects(USER, 'PACKAGE')) - ) - ''' - } - } - else if (objectType == "TYPE") { - sql = ''' - SELECT DISTINCT - object_type || '.' || object_name AS id, - object_type AS parent_id, - 1 AS leaf, - 1 AS generatable, - 1 AS multiselectable - FROM user_procedures - WHERE object_type = ? - AND procedure_name IS NOT NULL - ''' - } - else { - sql = ''' - SELECT object_type || '.' || object_name AS id, - object_type AS parent_id, - 1 AS leaf, - 1 AS generatable, - 1 AS multiselectable - FROM user_objects - WHERE object_type = ? - AND generated = 'N' - ''' - } - val nodes = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Node), #[objectType]) - return nodes - } - - /** - * Gets a list of oddgen's nodes as candidates to run utPLSQL tests. - * - * This functions must be called from an oddgen generator only, since the Node is not - * defined in the utPLSQL extension. - * - * @return list of oddgen nodes (complete hierarchy loaded eagerly) - * @throws DataAccessException if there is a problem - */ - def List runnables() { - var String sql - if (normalizedUtPlsqlVersionNumber >= FIRST_VERSION_WITH_ANNOTATION_API) { - // using API available since 3.1.3 - sql = ''' - WITH - test AS ( - SELECT object_owner, - object_name, - path AS suitepath, - count( - CASE - WHEN item_type = 'UT_TEST' THEN - 1 - ELSE - NULL - END - ) over (partition by object_owner, object_name) AS test_count, - item_type, - item_name, - item_description - FROM TABLE(ut_runner.get_suites_info(user)) - ), - suite_tree AS ( - SELECT null AS parent_id, - 'SUITE' AS id, - 'All Suites' AS name, - 'All utPLSQL test suites' AS description, - 'PACKAGE_FOLDER_ICON' AS iconName, - 'No' AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM dual - UNION ALL - SELECT DISTINCT - 'SUITE' AS parent_id, - object_owner || '.' || object_name AS id, - object_name AS name, - null AS description, - 'PACKAGE_ICON' AS iconName, - 'No' AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM test - WHERE item_type IN ('UT_TEST', 'UT_SUITE') - UNION ALL - SELECT object_owner || '.' || object_name AS parent_id, - object_owner || '.' || object_name || '.' || item_name AS id, - item_name AS name, - item_description AS description, - 'PROCEDURE_ICON' AS iconName, - 'Yes' AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM test - WHERE item_type = 'UT_TEST' - ), - suitepath_tree AS ( - SELECT NULL AS parent_id, - 'SUITEPATH' AS id, - 'All Suitepaths' AS name, - 'All utPLSQL test suitepathes' AS description, - 'FOLDER_ICON' AS iconName, - 'No' AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM dual - UNION ALL - SELECT CASE - WHEN regexp_replace(suitepath,'\.?\w+$','') IS NULL THEN - 'SUITEPATH' - ELSE - object_owner || ':' || regexp_replace(suitepath,'\.?\w+$','') - END AS parent_id, - object_owner || ':' || suitepath AS id, - item_name AS name, - item_description AS description, - CASE - WHEN item_type = 'UT_SUITE' AND test_count > 0 THEN - 'PACKAGE_ICON' - WHEN item_type = 'UT_TEST' THEN - 'PROCEDURE_ICON' - ELSE - 'FOLDER_ICON' - END AS iconName, - CASE item_type - WHEN 'UT_TEST' THEN - 'Yes' - ELSE - 'No' - END AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM test - ), - tree AS ( - SELECT parent_id, id, name, description, iconName, leaf, generatable, multiselectable, relevant - FROM suite_tree - UNION ALL - SELECT parent_id, id, name, description, iconName, leaf, generatable, multiselectable, relevant - FROM suitepath_tree - ) - SELECT parent_id, id, initcap(name) AS name, description, iconName, leaf, generatable, multiselectable, relevant - FROM tree - ''' - } else { - // using internal API (deprecated) - sql = ''' - WITH - base AS ( - SELECT rownum AS an_id, - o.object_owner, - o.object_type, - o.object_name, - lower(a.name) AS name, - a.text, - a.subobject_name - FROM table(«utplsqlSchema».ut_annotation_manager.get_annotated_objects(user, 'PACKAGE')) o - CROSS JOIN table(o.annotations) a - WHERE lower(a.name) in ('suite', 'suitepath', 'endcontext', 'test') - OR lower(a.name) = 'context' AND regexp_like(text, '(\w+)(\.\w+)*') - ), - suite AS ( - SELECT object_owner, object_type, object_name, text AS suite_description - FROM base - WHERE name = 'suite' - ), - suitepath as ( - SELECT object_owner, object_type, object_name, lower(text) AS suitepath - FROM base - WHERE name = 'suitepath' - ), - context_base AS ( - SELECT an_id, - lead(an_id) over (partition by object_owner, object_type, object_name order by an_id) AS an_id_end, - object_owner, - object_type, - object_name, - name, - lead(name) over (partition by object_owner, object_type, object_name order by an_id) AS name_end, - text as context - FROM base - WHERE name IN ('context', 'endcontext') - ), - context as ( - SELECT an_id, an_id_end, object_owner, object_type, object_name, context - FROM context_base - WHERE name = 'context' - AND name_end = 'endcontext' - ), - test AS ( - SELECT b.an_id, - b.object_owner, - b.object_type, - b.object_name, - p.suitepath, - c.context, - b.subobject_name, - b.text AS test_description - FROM base b - LEFT JOIN suitepath p - ON p.object_owner = b.object_owner - AND p.object_type = b.object_type - AND p.object_name = b.object_name - LEFT JOIN context c - ON c.object_owner = b.object_owner - AND c.object_type = b.object_type - AND c.object_name = b.object_name - AND b.an_id BETWEEN c.an_id AND c.an_id_end - WHERE name = 'test' - AND (b.object_owner, b.object_type, b.object_name) IN ( - SELECT object_owner, object_type, object_name - FROM suite - ) - ), - suite_tree AS ( - SELECT null AS parent_id, - 'SUITE' AS id, - 'All Suites' AS name, - 'All utPLSQL test suites' AS description, - 'PACKAGE_FOLDER_ICON' AS iconName, - 'No' AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM dual - UNION ALL - SELECT DISTINCT - 'SUITE' AS parent_id, - object_owner || '.' || object_name AS id, - object_name AS name, - null AS description, - 'PACKAGE_ICON' AS iconName, - 'No' AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM test - UNION ALL - SELECT object_owner || '.' || object_name AS parent_id, - object_owner || '.' || object_name || '.' || upper(subobject_name) AS id, - subobject_name AS name, - null AS description, - 'PROCEDURE_ICON' AS iconName, - 'Yes' AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM test - ), - suitepath_base AS ( - SELECT DISTINCT - suitepath - FROM suitepath - ), - gen AS ( - SELECT rownum AS pos - FROM xmltable('1 to 100') - ), - suitepath_part AS ( - SELECT DISTINCT - lower(substr(suitepath, 1, instr(suitepath || '.', '.', 1, g.pos) -1)) AS suitepath - FROM suitepath_base b - JOIN gen g - ON g.pos <= regexp_count(suitepath, '\w+') - ), - suitepath_tree AS ( - SELECT NULL AS parent_id, - 'SUITEPATH' AS id, - 'All Suitepaths' AS name, - 'All utPLSQL test suitepathes' AS description, - 'FOLDER_ICON' AS iconName, - 'No' AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM dual - UNION ALL - SELECT CASE - WHEN regexp_replace(suitepath,'\.?\w+$','') IS NULL THEN - 'SUITEPATH' - ELSE - USER || ':' || regexp_replace(suitepath,'\.?\w+$','') - END AS parent_id, - USER || ':' || suitepath AS id, - regexp_substr(suitepath, '\.?(\w+$)', 1, 1, NULL, 1) AS name, - null AS description, - 'FOLDER_ICON' AS iconName, - 'No' AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM suitepath_part - UNION ALL - SELECT DISTINCT - object_owner || ':' || suitepath AS parent_id, - object_owner || ':' || suitepath || '.' || lower(object_name) AS id, - object_name AS name, - null AS description, - 'PACKAGE_ICON' AS iconName, - 'No' AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM test - WHERE suitepath IS NOT NULL - UNION ALL - SELECT DISTINCT - object_owner || ':' || suitepath || '.' || lower(object_name) AS parent_id, - object_owner || ':' || suitepath || '.' || lower(object_name) || '.' || context AS id, - context AS name, - null AS description, - 'FOLDER_ICON' AS iconName, - 'No' AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM test - WHERE suitepath IS NOT NULL - AND context IS NOT NULL - UNION ALL - SELECT object_owner || ':' || suitepath || '.' || lower(object_name) || CASE WHEN context IS NOT NULL THEN '.' || context END AS parent_id, - object_owner || ':' || suitepath || '.' || lower(object_name) || CASE WHEN context IS NOT NULL THEN '.' || context END || '.' || lower(subobject_name) AS id, - subobject_name AS name, - null AS description, - 'PROCEDURE_ICON' AS iconName, - 'Yes' AS leaf, - 'Yes' AS generatable, - 'Yes' AS multiselectable, - 'Yes' AS relevant - FROM test - WHERE suitepath IS NOT NULL - ), - tree AS ( - SELECT parent_id, id, name, description, iconName, leaf, generatable, multiselectable, relevant - FROM suite_tree - UNION ALL - SELECT parent_id, id, name, description, iconName, leaf, generatable, multiselectable, relevant - FROM suitepath_tree - ) - SELECT parent_id, id, initcap(name) AS name, description, iconName, leaf, generatable, multiselectable, relevant - FROM tree - ''' - } - val nodes = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Node)) - return nodes - } - - /** - * enable DBMS_OUTPUT - * - * @throws DataAccessException if there is a problem - */ - def void enableDbmsOutput() { - // equivalent to "set serveroutput on size unlimited" - jdbcTemplate.update(''' - BEGIN - sys.dbms_output.enable(NULL); - END; - ''') - } - - /** - * disable DBMS_OUTPUT - * - * @throws DataAccessException if there is a problem - */ - def void disableDbmsOutput() { - jdbcTemplate.update(''' - BEGIN - sys.dbms_output.disable; - END; - ''') - } - - /** - * return the content of DBMS_OUTPUT as String - * - * @throws DataAccessException if there is a problem - */ - def String getDbmsOutput() { - return getDbmsOutput(1000) - } - - /** - * return the content of DBMS_OUTPUT as String - - * @param bufferSize maximum number of rows to be read from the DBMS_OUTPUT buffer in one network round trip - * @return content of DBMS_OUTPUT as String - * @throws DataAccessException if there is a problem - */ - def String getDbmsOutput(int bufferSize) { - val sb = new StringBuffer - val sql = ''' - BEGIN - sys.dbms_output.get_lines(?, ?); - END; - ''' - var OutputLines ret - do { - ret = jdbcTemplate.execute(sql, new CallableStatementCallback() { - override OutputLines doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException { - cs.registerOutParameter(1, Types.ARRAY, "DBMSOUTPUT_LINESARRAY"); - cs.registerOutParameter(2, Types.INTEGER) - cs.setInt(2, bufferSize) - cs.execute - val out = new OutputLines - out.lines = cs.getArray(1).array as String[] - out.numlines = cs.getInt(2) - return out - } - }) - for (i : 0 ..< ret.numlines) { - val line = ret.lines.get(i) - if (line !== null) { - sb.append(ret.lines.get(i)) - } - sb.append(System.lineSeparator) - } - } while (ret.numlines > 0) - return sb.toString - } - - /** - * gets the HTML code coverage report as String - * - * @param pathList utPLSQL path list - * @param schemaList list of schemas under tests. Current schema, if empty - * @param includeObjectList list of objects to be included for coverage analysis. All, if empty - * @param excludeObjectList list of objects to be excluded from coverage analysis. None, if empty - * @return HTML code coverage report in HTML format - * @throws DataAccessException if there is a problem - */ - def String htmlCodeCoverage(List pathList, List schemaList, List includeObjectList, List excludeObjectList) { - val sql = ''' - SELECT column_value - FROM table( - ut.run( - a_paths => ut_varchar2_list( - «FOR path : pathList SEPARATOR ", "» - '«path»' - «ENDFOR» - ), - «IF schemaList.size > 0» - a_coverage_schemes => ut_varchar2_list( - «FOR schema : schemaList SEPARATOR ", "» - '«schema»' - «ENDFOR» - ), - «ENDIF» - «IF includeObjectList.size > 0» - a_include_objects => ut_varchar2_list( - «FOR includeObject : includeObjectList SEPARATOR ", "» - '«includeObject»' - «ENDFOR» - ), - «ENDIF» - «IF excludeObjectList.size > 0» - a_exclude_objects => ut_varchar2_list( - «FOR excludeObject : excludeObjectList SEPARATOR ", "» - '«excludeObject»' - «ENDFOR» - ), - «ENDIF» - a_reporter => ut_coverage_html_reporter() - ) - ) - ''' - val lines = jdbcTemplate.queryForList(sql, String) - val sb = new StringBuilder - for (line : lines.filter[it !== null]) { - sb.append(line) - sb.append("\n") - } - return sb.toString - } - - /** - * gets dependencies of a given object. - * - * The result can be used as input for the includeObjectList in htmlCodeCoverage - * The scope is reduced to non-oracle maintained schemas. - * - * Oracle introduced the column ORACLE_MAINTAINED in 12.1. - * To simplify the query and compatibility the result of the following - * query is included - * - * SELECT '''' || listagg(username, ''', ''') || '''' AS oracle_maintained_users - * FROM dba_users - * WHERE oracle_maintained = 'Y' - * ORDER BY username; - * - * The result may include test packages - * - * @param name test package name - * @return list of dependencies in the current schema - */ - def List includes(String owner, String name) { - val sql = ''' - select referenced_owner || '.' || referenced_name AS dep_name - from «IF dbaViewAccessible»dba«ELSE»all«ENDIF»_dependencies - WHERE owner = upper(?) - AND name = upper(?) - AND referenced_owner NOT IN ( - 'SYS', 'SYSTEM', 'XS$NULL', 'OJVMSYS', 'LBACSYS', 'OUTLN', 'SYS$UMF', - 'DBSNMP', 'APPQOSSYS', 'DBSFWUSER', 'GGSYS', 'ANONYMOUS', 'CTXSYS', - 'SI_INFORMTN_SCHEMA', 'DVF', 'DVSYS', 'GSMADMIN_INTERNAL', 'ORDPLUGINS', - 'MDSYS', 'OLAPSYS', 'ORDDATA', 'XDB', 'WMSYS', 'ORDSYS', 'GSMCATUSER', - 'MDDATA', 'REMOTE_SCHEDULER_AGENT', 'SYSBACKUP', 'GSMUSER', 'APEX_PUBLIC_USER', - 'SYSRAC', 'AUDSYS', 'DIP', 'SYSKM', 'ORACLE_OCM', 'APEX_INSTANCE_ADMIN_USER', - 'SYSDG', 'FLOWS_FILES', 'ORDS_METADATA', 'ORDS_PUBLIC_USER' - ) - AND referenced_owner NOT LIKE 'APEX\_______' - AND referenced_type IN ('PACKAGE', 'TYPE', 'PROCEDURE', 'FUNCTION', 'TRIGGER') - ''' - val deps = jdbcTemplate.queryForList(sql, String, #[owner, name]) - return deps - } - - /** - * gets source of an object from the database via DBMS_METADATA - * - * @param owner owner of the object (schema) - * @param objectType expected object types are PACKAGE, PACKAGE BODY - * @param objectName name of the object - * @return the source code of the object - * @throws DataAccessException if there is a problem - */ - def getSource(String owner, String objectType, String objectName) { - // dbms_metadata uses slightly different objectTypes - val fixedObjectType = if (objectType == "PACKAGE") { - "PACKAGE_SPEC" - } else if (objectType == "PACKAGE BODY") { - "PACKAGE_BODY" - } else { - objectType - } - val sql = ''' - BEGIN - ? := sys.dbms_metadata.get_ddl( - schema => ?, - object_type => ?, - name => ? - ); - END; - ''' - val ret = jdbcTemplate.execute(sql, new CallableStatementCallback() { - override String doInCallableStatement(CallableStatement cs) throws SQLException, DataAccessException { - cs.registerOutParameter(1, Types.CLOB); - cs.setString(2, owner) - cs.setString(3, fixedObjectType) - cs.setString(4, objectName) - cs.execute - return cs.getString(1) - } - }) - return ret - } - - /** - * gets the object type of a database object - * - * The object types "PACKAGE BODY", "TYPE BODY" have higher priority. - * "PACKAGE" OR "TYPE" will be returned only when no body exists. - * - * @param owner owner of the object (schema) - * @param objectName name of the object - * @return the object type, e.g. PACKAGE BODY, TYPE BODY, PROCEDURE, FUNCTION - */ - def getObjectType(String owner, String objectName) { - val sql = ''' - SELECT object_type - FROM ( - SELECT object_type - FROM «IF dbaViewAccessible»dba«ELSE»all«ENDIF»_objects - WHERE owner = ? - AND object_name = ? - ORDER BY decode(object_type, 'PACKAGE', 10, 'TYPE', 10, 'SYNONYM', 20, 1) - ) - WHERE rownum = 1 - ''' - val objectType = jdbcTemplate.queryForObject(sql, #[owner, objectName], String) - return objectType - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/exception/GenericDatabaseAccessException.java b/sqldev/src/main/java/org/utplsql/sqldev/exception/GenericDatabaseAccessException.java new file mode 100644 index 00000000..a343592b --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/exception/GenericDatabaseAccessException.java @@ -0,0 +1,32 @@ +/* + * Copyright 2020 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.exception; + +public class GenericDatabaseAccessException extends RuntimeException { + private static final long serialVersionUID = -5489500390596695295L; + + public GenericDatabaseAccessException(String message) { + super(message); + } + + public GenericDatabaseAccessException(String message, Throwable cause) { + super(message, cause); + } + + public GenericDatabaseAccessException(Throwable cause) { + super(cause); + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/RealtimeReporterEvent.xtend b/sqldev/src/main/java/org/utplsql/sqldev/exception/GenericRuntimeException.java similarity index 53% rename from sqldev/src/main/java/org/utplsql/sqldev/model/runner/RealtimeReporterEvent.xtend rename to sqldev/src/main/java/org/utplsql/sqldev/exception/GenericRuntimeException.java index 5b6ec099..5a89ad13 100644 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/RealtimeReporterEvent.xtend +++ b/sqldev/src/main/java/org/utplsql/sqldev/exception/GenericRuntimeException.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Philipp Salvisberg + * Copyright 2020 Philipp Salvisberg * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -13,9 +13,20 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.utplsql.sqldev.model.runner +package org.utplsql.sqldev.exception; -import org.utplsql.sqldev.model.AbstractModel +public class GenericRuntimeException extends RuntimeException { + private static final long serialVersionUID = -6258053040039956647L; -abstract class RealtimeReporterEvent extends AbstractModel { + public GenericRuntimeException(String message) { + super(message); + } + + public GenericRuntimeException(String message, Throwable cause) { + super(message, cause); + } + + public GenericRuntimeException(Throwable cause) { + super(cause); + } } diff --git a/sqldev/src/main/java/org/utplsql/sqldev/menu/UtplsqlController.java b/sqldev/src/main/java/org/utplsql/sqldev/menu/UtplsqlController.java new file mode 100644 index 00000000..a7e74654 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/menu/UtplsqlController.java @@ -0,0 +1,525 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.menu; + +import java.awt.Component; +import java.net.URL; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.logging.Logger; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.swing.JEditorPane; + +import org.utplsql.sqldev.coverage.CodeCoverageReporter; +import org.utplsql.sqldev.dal.RealtimeReporterDao; +import org.utplsql.sqldev.dal.UtplsqlDao; +import org.utplsql.sqldev.exception.GenericRuntimeException; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.StringTools; +import org.utplsql.sqldev.model.URLTools; +import org.utplsql.sqldev.model.oddgen.GenContext; +import org.utplsql.sqldev.model.parser.PlsqlObject; +import org.utplsql.sqldev.model.preference.PreferenceModel; +import org.utplsql.sqldev.oddgen.TestTemplate; +import org.utplsql.sqldev.parser.UtplsqlParser; +import org.utplsql.sqldev.runner.UtplsqlRunner; +import org.utplsql.sqldev.runner.UtplsqlWorksheetRunner; + +import oracle.dbtools.raptor.navigator.db.DBNavigatorWindow; +import oracle.dbtools.raptor.navigator.db.DatabaseConnection; +import oracle.dbtools.raptor.navigator.impl.ChildObjectElement; +import oracle.dbtools.raptor.navigator.impl.DatabaseSourceNode; +import oracle.dbtools.raptor.navigator.impl.ObjectFolder; +import oracle.dbtools.raptor.navigator.impl.SchemaFolder; +import oracle.dbtools.raptor.navigator.plsql.PlSqlNode; +import oracle.dbtools.raptor.utils.Connections; +import oracle.dbtools.worksheet.editor.Worksheet; +import oracle.ide.Context; +import oracle.ide.Ide; +import oracle.ide.config.Preferences; +import oracle.ide.controller.Controller; +import oracle.ide.controller.IdeAction; +import oracle.ide.editor.Editor; +import oracle.ide.model.Node; +import oracle.ide.view.View; + +@SuppressWarnings("all") +public class UtplsqlController implements Controller { + private static final Logger logger = Logger.getLogger(UtplsqlController.class.getName()); + + public static int UTPLSQL_TEST_CMD_ID = (Ide.findCmdID("utplsql.test")).intValue(); + public static int UTPLSQL_COVERAGE_CMD_ID = (Ide.findCmdID("utplsql.coverage")).intValue(); + public static int UTPLSQL_GENERATE_CMD_ID = (Ide.findCmdID("utplsql.generate")).intValue(); + public static final IdeAction UTPLSQL_TEST_ACTION = IdeAction.get(UTPLSQL_TEST_CMD_ID); + public static final IdeAction UTPLSQL_COVERAGE_ACTION = IdeAction.get(UTPLSQL_COVERAGE_CMD_ID); + public static final IdeAction UTPLSQL_GENERATE_ACTION = IdeAction.get(UTPLSQL_GENERATE_CMD_ID); + + @Override + public boolean handleEvent(final IdeAction action, final Context context) { + try { + if (action.getCommandId() == UTPLSQL_TEST_CMD_ID) { + logger.finer(() -> "handle utplsql.test"); + runTest(context); + return true; + } else if (action.getCommandId() == UTPLSQL_COVERAGE_CMD_ID) { + logger.finer(() -> "handle utplsql.coverage"); + codeCoverage(context); + return true; + } else if (action.getCommandId() == UTPLSQL_GENERATE_CMD_ID) { + logger.finer(() -> "handle utplsql.generate"); + generateTest(context); + return true; + } + } catch (Exception e) { + final String msg = "Failed to handle event for action " + action.toString() + "."; + logger.severe(() -> msg); + throw new GenericRuntimeException(msg, e); + } + return false; + } + + @Override + public boolean update(final IdeAction action, final Context context) { + if (action.getCommandId() == UTPLSQL_TEST_CMD_ID || action.getCommandId() == UTPLSQL_COVERAGE_CMD_ID) { + final PreferenceModel preferences = PreferenceModel.getInstance(Preferences.getPreferences()); + action.setEnabled(false); + final View view = context.getView(); + if (view instanceof Editor) { + final Component component = ((Editor) view).getDefaultFocusComponent(); + if (component instanceof JEditorPane) { + if (preferences.isCheckRunUtplsqlTest()) { + final Node node = context.getNode(); + String connectionName = null; + String owner = null; + if (node instanceof DatabaseSourceNode) { + connectionName = ((DatabaseSourceNode) node).getConnectionName(); + owner = ((DatabaseSourceNode) node).getOwner(); + } else { + if (view instanceof Worksheet) { + connectionName = ((Worksheet) view).getConnectionName(); + } + } + logger.fine("connectionName: " + connectionName); + final String text = ((JEditorPane) component).getText(); + final Connection conn = DatabaseTools.getConnection(connectionName); + final UtplsqlParser parser = new UtplsqlParser(text, conn, owner); + if (!parser.getPathAt(((JEditorPane) component).getCaretPosition()).isEmpty()) { + action.setEnabled(true); + } + } else { + action.setEnabled(true); + } + } + } else if (view instanceof DBNavigatorWindow) { + action.setEnabled(true); + // disable action if a node in the selection is not runnable + for (int i = 0; i < context.getSelection().length; i++) { + logger.fine("section " + i + " is " + context.getSelection()[i].toString() + " of class " + + context.getSelection()[i].getClass().getName()); + if (action.isEnabled()) { + final Object element = context.getSelection()[i]; + final String connectionName = URLTools.getConnectionName(getURL(context)); + if (Connections.getInstance().isConnectionOpen(connectionName)) { + Connection conn = DatabaseTools.getConnection(connectionName); + final UtplsqlDao dao = new UtplsqlDao(conn); + if (preferences.isCheckRunUtplsqlTest() && dao.isUtAnnotationManagerInstalled()) { + if (element instanceof DatabaseConnection) { + final String schema = DatabaseTools.getSchema((DatabaseConnection) element); + action.setEnabled(dao.containsUtplsqlTest(schema)); + } else if (element instanceof SchemaFolder) { + final String schema = ((SchemaFolder) element).getSchemaName(); + action.setEnabled(dao.containsUtplsqlTest(schema)); + } else if (element instanceof ObjectFolder) { + final String schema = URLTools.getSchema(((ObjectFolder) element).getURL()); + action.setEnabled(dao.containsUtplsqlTest(schema)); + } else if (element instanceof PlSqlNode) { + final String schema = ((PlSqlNode) element).getOwner(); + final String objectName = ((PlSqlNode) element).getObjectName(); + action.setEnabled(dao.containsUtplsqlTest(schema, objectName)); + } else if (element instanceof ChildObjectElement) { + final String schema = URLTools.getSchema(((ChildObjectElement) element).getURL()); + final String objectName = URLTools.getMemberObject(((ChildObjectElement) element).getURL()); + final String subObjectName = ((ChildObjectElement) element).getShortLabel(); + action.setEnabled(dao.containsUtplsqlTest(schema, objectName, subObjectName)); + } + } + } else { + action.setEnabled(false); + } + } + } + } + return true; + } else if (action.getCommandId() == UTPLSQL_GENERATE_CMD_ID) { + action.setEnabled(false); + // enable if generation is possible + final View view = context.getView(); + if (view instanceof Editor) { + final Component component = ((Editor) view).getDefaultFocusComponent(); + if (component instanceof JEditorPane) { + final PreferenceModel preferences = PreferenceModel.getInstance(Preferences.getPreferences()); + if (preferences.isCheckGenerateUtplsqlTest()) { + final String text = ((JEditorPane) component).getText(); + final UtplsqlParser parser = new UtplsqlParser(text); + PlsqlObject plsqlObject = parser.getObjectAt(((JEditorPane) component).getCaretPosition()); + action.setEnabled(plsqlObject != null); + } else { + action.setEnabled(true); + } + } + } else if (view instanceof DBNavigatorWindow) { + // multiselection is not supported, use oddgen to generte tests for multiple objects + if (context.getSelection().length == 1) { + final Object element = context.getSelection()[0]; + if (element instanceof PlSqlNode) { + final String ot = ((PlSqlNode) element).getObjectType(); + if (ot.startsWith("PACKAGE") || ot.startsWith("TYPE") || "FUNCTION".equals(ot) || "PROCEDURE".equals(ot)) { + action.setEnabled(true); + } + } + } + } + } + return false; + } + + private String getPath(final Object element) { + String path = null; + if (element instanceof DatabaseConnection) { + path = DatabaseTools.getSchema((DatabaseConnection) element); + } else if (element instanceof SchemaFolder) { + path = ((SchemaFolder) element).getSchemaName(); + } else if (element instanceof ObjectFolder) { + path = URLTools.getSchema(((ObjectFolder) element).getURL()); + } else if (element instanceof PlSqlNode) { + final StringBuilder sb = new StringBuilder(); + sb.append(((PlSqlNode) element).getOwner()); + sb.append("."); + sb.append(((PlSqlNode) element).getObjectName()); + path = sb.toString(); + } else if (element instanceof ChildObjectElement) { + StringBuilder sb = new StringBuilder(); + sb.append(URLTools.getSchema(((ChildObjectElement) element).getURL())); + sb.append("."); + sb.append(URLTools.getMemberObject(((ChildObjectElement) element).getURL())); + sb.append("."); + sb.append( ((ChildObjectElement) element).getShortLabel()); + path = sb.toString(); + } else { + path = ""; + } + logger.fine("path: " + path); + return path; + } + + private ArrayList getPathList(final Context context) { + final ArrayList pathList = new ArrayList<>(); + for (int i = 0; i < context.getSelection().length; i++) { + final Object element = context.getSelection()[i]; + pathList.add(getPath(element)); + } + return pathList; + } + + private ArrayList getPathList(final String path) { + final ArrayList pathList = new ArrayList<>(); + pathList.add(path); + return pathList; + } + + private ArrayList dedupPathList(final List pathList) { + final HashSet set = new HashSet<>(); + for (final String path : pathList) { + set.add(path); + } + final ArrayList ret = new ArrayList<>(); + final Pattern p = Pattern.compile("(((([^\\.]+)\\.)?[^\\.]+)\\.)?[^\\.]+"); + for (final String path : set) { + final Matcher m = p.matcher(path); + if (m.matches()) { + final String parent1 = m.group(4); // user + final String parent2 = m.group(2); // user.package + if (parent1 == null || !set.contains(parent1)) { + if (parent2 == null || !set.contains(parent2)) { + ret.add(path); + } + } + } else { + logger.severe("path: " + path + " did not match " + p.toString() + ", this is unexected!"); + } + } + return ret; + } + + private URL getURL(final Context context) { + URL url = null; + final Object element = context.getSelection()[0]; + if (element instanceof DatabaseConnection) { + url = ((DatabaseConnection) element).getURL(); + } else if (element instanceof SchemaFolder) { + url = ((SchemaFolder) element).getURL(); + } else if (element instanceof ObjectFolder) { + url = ((ObjectFolder) element).getURL(); + } else if (element instanceof PlSqlNode) { + url = ((PlSqlNode) element).getURL(); + } else if (element instanceof ChildObjectElement) { + url = ((ChildObjectElement) element).getURL(); + } + logger.fine("url: " + url); + return url; + } + + private void populateGenContext(final GenContext genContext, final PreferenceModel preferences) { + genContext.setTestPackagePrefix(preferences.getTestPackagePrefix().toLowerCase()); + genContext.setTestPackageSuffix(preferences.getTestPackageSuffix().toLowerCase()); + genContext.setTestUnitPrefix(preferences.getTestUnitPrefix().toLowerCase()); + genContext.setTestUnitSuffix(preferences.getTestUnitSuffix().toLowerCase()); + genContext.setNumberOfTestsPerUnit(preferences.getNumberOfTestsPerUnit()); + genContext.setGenerateComments(preferences.isGenerateComments()); + genContext.setDisableTests(preferences.isDisableTests()); + genContext.setSuitePath(preferences.getSuitePath().toLowerCase()); + genContext.setIndentSpaces(preferences.getIndentSpaces()); + } + + private GenContext getGenContext(final Context context) { + final String connectionName = URLTools.getConnectionName(getURL(context)); + final GenContext genContext = new GenContext(); + if (Connections.getInstance().isConnectionOpen(connectionName)) { + genContext.setConn(DatabaseTools.getConnection(connectionName)); + final Object element = context.getSelection()[0]; + if (element instanceof PlSqlNode) { + genContext.setObjectType(((PlSqlNode) element).getObjectType().replace(" BODY", "")); + genContext.setObjectName(((PlSqlNode) element).getObjectName()); + final PreferenceModel preferences = PreferenceModel.getInstance(Preferences.getPreferences()); + populateGenContext(genContext, preferences); + } + } + return genContext; + } + + public void runTest(final Context context) { + final View view = context.getView(); + final Node node = context.getNode(); + final PreferenceModel preferences = PreferenceModel.getInstance(Preferences.getPreferences()); + final StringBuilder sb = new StringBuilder(); + sb.append("Run utPLSQL from view "); + sb.append(view != null ? view.getClass().getName() : "???"); + sb.append(" and node "); + sb.append(node != null ? node.getClass().getName() : "???"); + sb.append("."); + logger.finer(() -> sb.toString()); + if (view instanceof Editor) { + final Component component = ((Editor) view).getDefaultFocusComponent(); + if (component instanceof JEditorPane) { + String connectionName = null; + String owner = null; + if (node instanceof DatabaseSourceNode) { + connectionName = ((DatabaseSourceNode) node).getConnectionName(); + owner = ((DatabaseSourceNode) node).getOwner(); + } else { + if (view instanceof Worksheet) { + connectionName = ((Worksheet) view).getConnectionName(); + } + } + logger.fine("connectionName: " + connectionName); + final Connection conn = DatabaseTools.getConnection(connectionName); + String text = ((JEditorPane) component).getText(); + final UtplsqlParser parser = new UtplsqlParser(text, conn, owner); + final int position = ((JEditorPane) component).getCaretPosition(); + final String path = parser.getPathAt(position); + final RealtimeReporterDao rrDao = new RealtimeReporterDao(conn); + if (preferences.isUseRealtimeReporter() && rrDao.isSupported()) { + final UtplsqlRunner runner = new UtplsqlRunner(getPathList(path), connectionName); + runner.runTestAsync(); + } else { + final UtplsqlWorksheetRunner worksheet = new UtplsqlWorksheetRunner(getPathList(path), connectionName); + worksheet.runTestAsync(); + } + } + } else if (view instanceof DBNavigatorWindow) { + final URL url = getURL(context); + if (url != null) { + final String connectionName = URLTools.getConnectionName(url); + logger.fine("connectionName: " + connectionName); + final Connection conn = DatabaseTools.getConnection(connectionName); + final RealtimeReporterDao rrDao = new RealtimeReporterDao(conn); + final ArrayList pathList = dedupPathList(getPathList(context)); + if (preferences.isUseRealtimeReporter() && rrDao.isSupported()) { + final UtplsqlRunner runner = new UtplsqlRunner(pathList, connectionName); + runner.runTestAsync(); + } else { + final UtplsqlWorksheetRunner worksheet = new UtplsqlWorksheetRunner(pathList, connectionName); + worksheet.runTestAsync(); + } + } + } + } + + public List dependencies(final String name, final String connectionName) { + List ret = null; + if (connectionName != null) { + final String owner = DatabaseTools.getSchema(connectionName); + ret = dependencies(owner, name, connectionName); + } + return ret; + } + + public List dependencies(final String owner, final String name, final String connectionName) { + List ret = null; + if (connectionName != null) { + Connection conn = DatabaseTools.getConnection(connectionName); + final UtplsqlDao dao = new UtplsqlDao(conn); + ret = dao.includes(owner, name); + } + return ret; + } + + public List dependencies(final Context context, final String connectionName) { + final HashSet ret = new HashSet(); + for (int i = 0; i < context.getSelection().length; i++) { + final Object element = context.getSelection()[i]; + if (element instanceof PlSqlNode) { + final String owner = ((PlSqlNode) element).getOwner(); + final String objectName = ((PlSqlNode) element).getObjectName(); + final List dep = dependencies(owner, objectName, connectionName); + ret.addAll(dep); + } else { + if (element instanceof ChildObjectElement) { + final String owner = URLTools.getSchema(((ChildObjectElement) element).getURL()); + final String objectName = URLTools.getMemberObject(((ChildObjectElement) element).getURL()); + final List dep = dependencies(owner, objectName, connectionName); + ret.addAll(dep); + } + } + } + return ret.stream().sorted().collect(Collectors.toList()); + } + + public void codeCoverage(final Context context) { + final View view = context.getView(); + final Node node = context.getNode(); + final StringBuilder sb = new StringBuilder(); + sb.append("Code coverage from view "); + sb.append(view != null ? view.getClass().getName() : "???"); + sb.append(" and node "); + sb.append(node != null ? node.getClass().getName() : "???"); + sb.append("."); + logger.finer(() -> sb.toString()); + if (view instanceof Editor) { + final Component component = ((Editor) view).getDefaultFocusComponent(); + if (component instanceof JEditorPane) { + String connectionName = null; + String owner = null; + if (node instanceof DatabaseSourceNode) { + connectionName = ((DatabaseSourceNode) node).getConnectionName(); + } else if (view instanceof Worksheet) { + connectionName = ((Worksheet) view).getConnectionName(); + } + logger.fine("connectionName: " + connectionName); + final PreferenceModel preferences = PreferenceModel.getInstance(Preferences.getPreferences()); + String text = ((JEditorPane) component).getText(); + Connection conn = null; + if (preferences.isCheckRunUtplsqlTest()) { + conn = DatabaseTools.getConnection(connectionName); + } else { + conn = null; + } + final UtplsqlParser parser = new UtplsqlParser(text, conn, owner); + final int position = ((JEditorPane) component).getCaretPosition(); + final String path = parser.getPathAt(position); + final PlsqlObject object = parser.getObjectAt(position); + final List includeObjectList = dependencies(object.getName(), connectionName); + final CodeCoverageReporter reporter = new CodeCoverageReporter(getPathList(path), includeObjectList, connectionName); + reporter.showParameterWindow(); + } + } else if (view instanceof DBNavigatorWindow) { + logger.finer("Code coverage from DB navigator"); + final URL url = getURL(context); + if (url != null) { + final String connectionName = URLTools.getConnectionName(url); + logger.fine(() -> "connectionName: " + connectionName); + final ArrayList pathList = dedupPathList(getPathList(context)); + logger.fine(() -> "pathlist: " + StringTools.getSimpleCSV(pathList)); + final List includeObjectList = dependencies(context, connectionName); + logger.finer(() -> "includeObjectList: " + StringTools.getSimpleCSV(includeObjectList)); + final CodeCoverageReporter reporter = new CodeCoverageReporter(pathList, includeObjectList, connectionName); + logger.finer(() -> "showing code coverage dialog"); + reporter.showParameterWindow(); + logger.finer(() -> "code coverage dialog shown"); + } else { + logger.warning("url is null"); + } + } + } + + public void generateTest(final Context context) { + final View view = context.getView(); + final Node node = context.getNode(); + final StringBuilder sb = new StringBuilder(); + sb.append("Generate utPLSQL test from view "); + sb.append(view != null ? view.getClass().getName() : "???"); + sb.append(" and node "); + sb.append(node != null ? node.getClass().getName() : "???"); + sb.append("."); + logger.finer(() -> sb.toString()); + if (view instanceof Editor) { + final Component component = ((Editor) view).getDefaultFocusComponent(); + if (component instanceof JEditorPane) { + String connectionName = null; + if (node instanceof DatabaseSourceNode) { + connectionName = ((DatabaseSourceNode) node).getConnectionName(); + } else if (view instanceof Worksheet) { + connectionName = ((Worksheet) view).getConnectionName(); + } + if (connectionName != null) { + if (Connections.getInstance().isConnectionOpen(connectionName)) { + final GenContext genContext = new GenContext(); + genContext.setConn(DatabaseTools.getConnection(connectionName)); + String text = ((JEditorPane) component).getText(); + final UtplsqlParser parser = new UtplsqlParser(text); + final int position = ((JEditorPane) component).getCaretPosition(); + final PlsqlObject obj = parser.getObjectAt(position); + if (obj != null) { + genContext.setObjectType(obj.getType().toUpperCase()); + genContext.setObjectName(obj.getName().toUpperCase()); + final PreferenceModel preferences = PreferenceModel.getInstance(Preferences.getPreferences()); + populateGenContext(genContext, preferences); + final TestTemplate testTemplate = new TestTemplate(genContext); + final String code = testTemplate.generate().toString(); + UtplsqlWorksheetRunner.openWithCode(code, connectionName); + } + } + } + } + } else { + if (view instanceof DBNavigatorWindow) { + final URL url = getURL(context); + if (url != null) { + final String connectionName = URLTools.getConnectionName(url); + GenContext genContext = getGenContext(context); + final TestTemplate testTemplate = new TestTemplate(genContext); + final String code = testTemplate.generate().toString(); + UtplsqlWorksheetRunner.openWithCode(code, connectionName); + } + } + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/menu/UtplsqlController.xtend b/sqldev/src/main/java/org/utplsql/sqldev/menu/UtplsqlController.xtend deleted file mode 100644 index 28eadd5d..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/menu/UtplsqlController.xtend +++ /dev/null @@ -1,448 +0,0 @@ -/* Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.menu - -import java.net.URL -import java.util.ArrayList -import java.util.HashSet -import java.util.List -import java.util.logging.Logger -import java.util.regex.Pattern -import javax.swing.JEditorPane -import oracle.dbtools.raptor.navigator.db.DBNavigatorWindow -import oracle.dbtools.raptor.navigator.db.DatabaseConnection -import oracle.dbtools.raptor.navigator.impl.ChildObjectElement -import oracle.dbtools.raptor.navigator.impl.DatabaseSourceNode -import oracle.dbtools.raptor.navigator.impl.ObjectFolder -import oracle.dbtools.raptor.navigator.impl.SchemaFolder -import oracle.dbtools.raptor.navigator.plsql.PlSqlNode -import oracle.dbtools.raptor.utils.Connections -import oracle.dbtools.worksheet.editor.Worksheet -import oracle.ide.Context -import oracle.ide.Ide -import oracle.ide.config.Preferences -import oracle.ide.controller.Controller -import oracle.ide.controller.IdeAction -import oracle.ide.editor.Editor -import org.utplsql.sqldev.coverage.CodeCoverageReporter -import org.utplsql.sqldev.dal.RealtimeReporterDao -import org.utplsql.sqldev.dal.UtplsqlDao -import org.utplsql.sqldev.model.URLTools -import org.utplsql.sqldev.model.oddgen.GenContext -import org.utplsql.sqldev.model.preference.PreferenceModel -import org.utplsql.sqldev.oddgen.TestTemplate -import org.utplsql.sqldev.parser.UtplsqlParser -import org.utplsql.sqldev.runner.UtplsqlRunner -import org.utplsql.sqldev.runner.UtplsqlWorksheetRunner - -class UtplsqlController implements Controller { - static final Logger logger = Logger.getLogger(UtplsqlController.name); - val extension URLTools urlTools = new URLTools - - public static int UTPLSQL_TEST_CMD_ID = Ide.findCmdID("utplsql.test") - public static int UTPLSQL_COVERAGE_CMD_ID = Ide.findCmdID("utplsql.coverage") - public static int UTPLSQL_GENERATE_CMD_ID = Ide.findCmdID("utplsql.generate") - public static final IdeAction UTPLSQL_TEST_ACTION = IdeAction.get(UtplsqlController.UTPLSQL_TEST_CMD_ID) - public static final IdeAction UTPLSQL_COVERAGE_ACTION = IdeAction.get(UtplsqlController.UTPLSQL_COVERAGE_CMD_ID) - public static final IdeAction UTPLSQL_GENERATE_ACTION = IdeAction.get(UtplsqlController.UTPLSQL_GENERATE_CMD_ID) - - override handleEvent(IdeAction action, Context context) { - try { - if (action.commandId === UTPLSQL_TEST_CMD_ID) { - logger.finer("handle utplsql.test") - runTest(context) - return true - } else if (action.commandId === UTPLSQL_COVERAGE_CMD_ID) { - logger.finer("handle utplsql.coverage") - codeCoverage(context) - return true - } else if (action.commandId === UTPLSQL_GENERATE_CMD_ID) { - logger.finer("handle utplsql.generate") - generateTest(context) - return true - } - } catch (Exception e) { - logger.severe("Failed to handle event due to exception " + e?.message) - } - return false - } - - override update(IdeAction action, Context context) { - if (action.commandId === UTPLSQL_TEST_CMD_ID || action.commandId === UTPLSQL_COVERAGE_CMD_ID) { - val preferences = PreferenceModel.getInstance(Preferences.preferences) - action.enabled = false - val view = context.view - if (view instanceof Editor) { - val component = view.defaultFocusComponent - if (component instanceof JEditorPane) { - if (preferences.checkRunUtplsqlTest) { - val node = context.node - var String connectionName = null; - var String owner = null; - if (node instanceof DatabaseSourceNode) { - connectionName = node.connectionName - owner = node.owner - } else if (view instanceof Worksheet) { - connectionName = view.connectionName - } - logger.fine('''connectionName: «connectionName»''') - val parser = new UtplsqlParser(component.text, Connections.instance.getConnection(connectionName), owner) - if (!parser.getPathAt(component.caretPosition).empty) { - action.enabled = true - } - } else { - action.enabled = true - } - } - } else if (view instanceof DBNavigatorWindow) { - action.enabled = true - // disable action if a node in the selection is not runnable - for (i : 0 ..< context.selection.length) { - logger.fine('''section «i» is «context.selection.get(i).toString» of class «context.selection.get(i).class.name»''') - if (action.enabled) { - val element = context.selection.get(i) - if (Connections.instance.isConnectionOpen(context.URL.connectionName)) { - val dao = new UtplsqlDao(Connections.instance.getConnection(context.URL.connectionName)) - if (preferences.checkRunUtplsqlTest && dao.utAnnotationManagerInstalled) { - if (element instanceof DatabaseConnection) { - action.enabled = dao.containsUtplsqlTest(element.connection.schema) - } else if (element instanceof SchemaFolder) { - action.enabled = dao.containsUtplsqlTest(element.schemaName) - } else if (element instanceof ObjectFolder) { - action.enabled = dao.containsUtplsqlTest(element.URL.schema) - } else if (element instanceof PlSqlNode) { - action.enabled = dao.containsUtplsqlTest(element.owner, element.objectName) - } else if (element instanceof ChildObjectElement) { - action.enabled = dao.containsUtplsqlTest(element.URL.schema, element.URL.memberObject, element.shortLabel) - } - } - } else { - action.enabled = false - } - } - } - } - return true - } else if (action.commandId === UTPLSQL_GENERATE_CMD_ID) { - action.enabled = false - // enable if generation is possible - val view = context.view - if (view instanceof Editor) { - val component = view.defaultFocusComponent - if (component instanceof JEditorPane) { - val preferences = PreferenceModel.getInstance(Preferences.preferences) - if (preferences.checkGenerateUtplsqlTest) { - val parser = new UtplsqlParser(component.text) - action.enabled = parser.getObjectAt(component.caretPosition) !== null - } else { - action.enabled = true - } - } - } else if (view instanceof DBNavigatorWindow) { - // multiselection is not supported, use oddgen to generte tests for multiple objects - if (context.selection.length == 1) { - val element = context.selection.get(0) - if (element instanceof PlSqlNode) { - val ot = element.objectType - if (ot.startsWith("PACKAGE") || ot.startsWith("TYPE") || ot == "FUNCTION" || ot == "PROCEDURE") { - action.enabled = true - } - } - } - } - } - return false - } - - private def getPath(Object element) { - var String path - if (element instanceof DatabaseConnection) { - path = element.connection.schema - } else if (element instanceof SchemaFolder) { - path = element.schemaName - } else if (element instanceof ObjectFolder) { - path = element.URL.schema - } else if (element instanceof PlSqlNode) { - path = '''«element.owner».«element.objectName»''' - } else if (element instanceof ChildObjectElement) { - path = '''«element.URL.schema».«element.URL.memberObject».«element.shortLabel»''' - } else { - path = "" - } - logger.fine('''path: «path»''') - return path - } - - private def getPathList(Context context) { - val pathList = new ArrayList() - for (i : 0 ..< context.selection.length) { - val element = context.selection.get(i) - pathList.add(element.path) - } - return pathList - } - - private def getPathList(String path) { - val pathList = new ArrayList - pathList.add(path) - return pathList - } - - private def dedupPathList(List pathList) { - val set = new HashSet - for (path : pathList) { - set.add(path) - } - val ret = new ArrayList - val p = Pattern.compile("(((([^\\.]+)\\.)?[^\\.]+)\\.)?[^\\.]+") - for (path : set) { - val m = p.matcher(path) - if (m.matches()) { - val parent1 = m.group(4) // user - val parent2 = m.group(2) // user.package - if (parent1 === null || !set.contains(parent1)) { - if (parent2 === null || !set.contains(parent2)) { - ret.add(path) - } - } - } else { - logger.severe('''path: «path» did not match «p.toString», this is unexected!''') - } - } - return ret - } - - private def getURL(Context context) { - var URL url - val element = context.selection.get(0) - if (element instanceof DatabaseConnection) { - url = element.URL - } else if (element instanceof SchemaFolder) { - url = element.URL - } else if (element instanceof ObjectFolder) { - url = element.URL - } else if (element instanceof PlSqlNode) { - url = element.URL - } else if (element instanceof ChildObjectElement) { - url = element.URL - } - logger.fine('''url: «url»''') - return url - } - - private def void populateGenContext(GenContext genContext, PreferenceModel preferences) { - genContext.testPackagePrefix = preferences.testPackagePrefix.toLowerCase - genContext.testPackageSuffix = preferences.testPackageSuffix.toLowerCase - genContext.testUnitPrefix = preferences.testUnitPrefix.toLowerCase - genContext.testUnitSuffix = preferences.testUnitSuffix.toLowerCase - genContext.numberOfTestsPerUnit = preferences.numberOfTestsPerUnit - genContext.generateComments = preferences.generateComments - genContext.disableTests = preferences.disableTests - genContext.suitePath = preferences.suitePath.toLowerCase - genContext.indentSpaces = preferences.indentSpaces - } - - private def getGenContext(Context context) { - val connectionName = context.URL.connectionName - val genContext = new GenContext - if (Connections.instance.isConnectionOpen(connectionName)) { - genContext.conn = Connections.instance.getConnection(connectionName) - val element = context.selection.get(0) - if (element instanceof PlSqlNode) { - genContext.objectType = element.objectType.replace(" BODY", "") - genContext.objectName = element.objectName - val preferences = PreferenceModel.getInstance(Preferences.preferences) - populateGenContext(genContext, preferences) - } - } - return genContext - } - - def runTest(Context context) { - val view = context.view - val node = context.node - val preferences = PreferenceModel.getInstance(Preferences.preferences) - logger.finer('''Run utPLSQL from view «view?.class?.name» and node «node?.class?.name».''') - if (view instanceof Editor) { - val component = view.defaultFocusComponent - if (component instanceof JEditorPane) { - var String connectionName = null; - var String owner = null; - if (node instanceof DatabaseSourceNode) { - connectionName = node.connectionName - owner = node.owner - } else if (view instanceof Worksheet) { - connectionName = view.connectionName - } - logger.fine('''connectionName: «connectionName»''') - // issue 59 - always use a connection to ensure the utPL/SQL annotation API is used - val conn = Connections.instance.getConnection(connectionName) - val parser = new UtplsqlParser(component.text, conn, owner) - val position = component.caretPosition - val path = parser.getPathAt(position) - val rrDao = new RealtimeReporterDao(conn) - if (preferences.useRealtimeReporter && rrDao.supported) { - val runner = new UtplsqlRunner(path.pathList, connectionName) - runner.runTestAsync - - } else { - val worksheet = new UtplsqlWorksheetRunner(path.pathList, connectionName) - worksheet.runTestAsync - } - } - } else if (view instanceof DBNavigatorWindow) { - val url=context.URL - if (url !== null) { - val connectionName = url.connectionName - logger.fine('''connectionName: «connectionName»''') - val conn = Connections.instance.getConnection(connectionName) - val rrDao = new RealtimeReporterDao(conn) - val pathList=context.pathList.dedupPathList - if (preferences.useRealtimeReporter && rrDao.supported) { - val runner = new UtplsqlRunner(pathList, connectionName) - runner.runTestAsync - } else { - val worksheet = new UtplsqlWorksheetRunner(pathList, connectionName) - worksheet.runTestAsync - } - } - } - } - - def List dependencies(String name, String connectionName) { - var List ret = null - if (connectionName !== null) { - val owner = Connections.instance.getConnection(connectionName).schema - ret = dependencies(owner, name, connectionName) - } - return ret - } - - def List dependencies(String owner, String name, String connectionName) { - var List ret = null - if (connectionName !== null) { - val dao = new UtplsqlDao(Connections.instance.getConnection(connectionName)) - ret = dao.includes(owner, name) - } - return ret - } - - def List dependencies(Context context, String connectionName) { - val HashSet ret = new HashSet - for (i : 0 ..< context.selection.length) { - val element = context.selection.get(i) - if (element instanceof PlSqlNode) { - val dep = dependencies(element.owner, element.objectName, connectionName) - for (d : dep) { - ret.add(d) - } - } else if (element instanceof ChildObjectElement) { - val dep = dependencies(element.URL.schema, element.URL.memberObject, connectionName) - for (d : dep) { - ret.add(d) - } - } - } - return ret.toList.sortBy[it] - } - - def codeCoverage(Context context) { - val view = context.view - val node = context.node - logger.finer('''Code coverage from view «view?.class?.name» and node «node?.class?.name».''') - if (view instanceof Editor) { - val component = view.defaultFocusComponent - if (component instanceof JEditorPane) { - var String connectionName = null; - var String owner = null; - if (node instanceof DatabaseSourceNode) { - connectionName = node.connectionName - } else if (view instanceof Worksheet) { - connectionName = view.connectionName - } - logger.fine('''connectionName: «connectionName»''') - val preferences = PreferenceModel.getInstance(Preferences.preferences) - val parser = new UtplsqlParser(component.text, if (preferences.checkRunUtplsqlTest) {Connections.instance.getConnection(connectionName)} else {null}, owner) - val position = component.caretPosition - val path = parser.getPathAt(position) - val object = parser.getObjectAt(position) - val includeObjectList = dependencies(object.name, connectionName) - val reporter = new CodeCoverageReporter(path.pathList, includeObjectList, connectionName) - reporter.showParameterWindow - } - } else if (view instanceof DBNavigatorWindow) { - logger.finer("Code coverage from DB navigator") - val url=context.URL - if (url !== null) { - val connectionName = url.connectionName - logger.fine('''connectionName: «connectionName»''') - val pathList=context.pathList.dedupPathList - logger.finer('''pathList: «pathList»''') - val includeObjectList = dependencies(context, connectionName) - logger.finer('''includeObjectList: «includeObjectList»''') - val reporter = new CodeCoverageReporter(pathList, includeObjectList, connectionName) - logger.finer("showing code coverage dialog") - reporter.showParameterWindow - logger.finer("code coverage dialog shown") - } else { - logger.warning('''url is null''') - } - } - } - - def generateTest(Context context) { - val view = context.view - val node = context.node - logger.finer('''Generate utPLSQL test from view «view?.class?.name» and node «node?.class?.name».''') - if (view instanceof Editor) { - val component = view.defaultFocusComponent - if (component instanceof JEditorPane) { - var String connectionName = null; - if (node instanceof DatabaseSourceNode) { - connectionName = node.connectionName - } else if (view instanceof Worksheet) { - connectionName = view.connectionName - } - if (connectionName !== null) { - if (Connections.instance.isConnectionOpen(connectionName)) { - val genContext = new GenContext - genContext.conn = Connections.instance.getConnection(connectionName) - val parser = new UtplsqlParser(component.text) - val position = component.caretPosition - val obj = parser.getObjectAt(position) - if (obj !== null) { - genContext.objectType = obj.type.toUpperCase - genContext.objectName = obj.name.toUpperCase - val preferences = PreferenceModel.getInstance(Preferences.preferences) - populateGenContext(genContext, preferences) - val testTemplate = new TestTemplate(genContext) - val code = testTemplate.generate.toString - UtplsqlWorksheetRunner.openWithCode(code, connectionName) - } - } - } - } - - } else if (view instanceof DBNavigatorWindow) { - val url=context.URL - if (url !== null) { - val connectionName = url.connectionName - val testTemplate = new TestTemplate(context.genContext) - val code = testTemplate.generate.toString - UtplsqlWorksheetRunner.openWithCode(code, connectionName) - } - } - } -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/DatabaseTools.java b/sqldev/src/main/java/org/utplsql/sqldev/model/DatabaseTools.java new file mode 100644 index 00000000..9c2365ea --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/DatabaseTools.java @@ -0,0 +1,173 @@ +/* + * Copyright 2020 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model; + +import java.io.IOException; +import java.sql.Connection; +import java.sql.SQLException; + +import javax.sql.DataSource; + +import org.springframework.core.task.SimpleAsyncTaskExecutor; +import org.utplsql.sqldev.exception.GenericDatabaseAccessException; +import org.utplsql.sqldev.exception.GenericRuntimeException; + +import oracle.dbtools.raptor.navigator.db.DatabaseConnection; +import oracle.dbtools.raptor.utils.Connections; +import oracle.javatools.db.DBException; +import oracle.jdeveloper.db.ConnectionException; + +public class DatabaseTools { + // do not instantiate this class + private DatabaseTools() { + super(); + } + + public static Connection getConnection(DataSource dataSource) { + try { + return dataSource.getConnection(); + } catch (SQLException e) { + throw new GenericDatabaseAccessException("Error getting connection.", e); + } + } + + public static Connection getConnection(DatabaseConnection conn) { + try { + return conn.getConnection(); + } catch (IOException e) { + final String msg = "Error getting connection for " + conn.getConnectionName() + "."; + throw new GenericDatabaseAccessException(msg, e); + } + } + + public static Connection getConnection(String connectionName) { + try { + return Connections.getInstance().getConnection(connectionName); + } catch (DBException e) { + final String msg = "Error getting connection for " + connectionName + "."; + throw new GenericDatabaseAccessException(msg, e); + } + } + + public static Connection cloneConnection(String connectionName) { + final Connection conn = getConnection(connectionName); + try { + return Connections.getInstance().cloneConnection(conn); + } catch (ConnectionException e) { + final String msg = "Error cloning connection " + connectionName + "."; + throw new GenericDatabaseAccessException(msg, e); + } + } + + private static String createTemporaryConnection(String connectionName) { + try { + return Connections.getInstance().createTemporaryConnection(connectionName); + } catch (Throwable e) { + final String msg = "Error creating temporary connection based on " + connectionName + "."; + throw new GenericDatabaseAccessException(msg, e); + } + } + + private static String createPrivateConnection(String connectionName) { + try { + return Connections.getInstance().createPrivateConnection(connectionName); + } catch (Throwable e) { + final String msg = "Error creating private connection based on " + connectionName + "."; + throw new GenericDatabaseAccessException(msg, e); + } + } + + public static String createTemporaryOrPrivateConnection(String connectionName) { + // Private connections are closed in SQL Developer < 17.4.0 when the worksheet + // is closed, but in SQL Developer > 17.4.0 private connections are not closed. + // Temporary connections have been introduced in SQL Developer 17.4.0. They will + // be always closed, when a worksheet is closed. + // Hence we try to use temporary connections whenever possible. See also + // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/47 . + try { + return createTemporaryConnection(connectionName); + } catch (GenericDatabaseAccessException e) { + return createPrivateConnection(connectionName); + } + } + + public static boolean isConnectionClosed(Connection conn) { + try { + return conn.isClosed(); + } catch (SQLException e) { + throw new GenericDatabaseAccessException("Error getting status of connection.", e); + } + } + + public static void closeConnection(Connection conn) { + if (!isConnectionClosed(conn)) { + try { + conn.close(); + } catch (SQLException e) { + throw new GenericDatabaseAccessException("Could not close connection."); + } + } + } + + public static void abortConnection(Connection conn) { + final SimpleAsyncTaskExecutor taskExecutor = new SimpleAsyncTaskExecutor(); + try { + conn.abort(taskExecutor); + } catch (SQLException e) { + throw new GenericDatabaseAccessException("Could not abort connection."); + } + } + + public static String getSchema(Connection conn) { + try { + return conn.getSchema(); + } catch (SQLException e) { + throw new GenericRuntimeException("Error getting schema name of connection.", e); + } + } + + public static String getUser(Connection conn) { + try { + return conn.getMetaData().getUserName(); + } catch (SQLException e) { + throw new GenericRuntimeException("Error getting user name of connection.", e); + } + } + + public static String getSchema(DatabaseConnection conn) { + return getSchema(getConnection(conn)); + } + + + public static String getSchema(String connectionName) { + return getSchema(getConnection(connectionName)); + } + + public static boolean isSupported(final Connection conn) { + try { + boolean ret = false; + if (conn != null && conn.getMetaData().getDatabaseProductName().startsWith("Oracle") + && (conn.getMetaData().getDatabaseMajorVersion() == 11 + && conn.getMetaData().getDatabaseMinorVersion() >= 2 + || conn.getMetaData().getDatabaseMajorVersion() > 11)) { + ret = true; + } + return ret; + } catch (SQLException e) { + throw new GenericDatabaseAccessException("Error while getting product version of connection.", e); + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/FileTools.java b/sqldev/src/main/java/org/utplsql/sqldev/model/FileTools.java new file mode 100644 index 00000000..02b32e5f --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/FileTools.java @@ -0,0 +1,57 @@ +/* + * Copyright 2020 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model; + +import java.io.IOException; +import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.utplsql.sqldev.exception.GenericRuntimeException; + +public class FileTools { + // do not instantiate this class + private FileTools() { + super(); + } + + public static byte[] readFile(Path path) { + try { + return Files.readAllBytes(path); + } catch (IOException e) { + final String msg = "Cannot read file " + path.toString() + "."; + throw new GenericRuntimeException(msg, e); + } + } + + public static void writeFile(Path path, byte[] bytes) { + try { + Files.write(path, bytes); + } catch (IOException e) { + final String msg = "Cannot write file " + path.toString() + "."; + throw new GenericRuntimeException(msg, e); + } + } + + public static void writeFile(Path path, Iterable lines, Charset cs) { + try { + Files.write(path, lines, cs); + } catch (IOException e) { + final String msg = "Cannot write file " + path.toString() + "."; + throw new GenericRuntimeException(msg, e); + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/JsonToStringStyler.java b/sqldev/src/main/java/org/utplsql/sqldev/model/JsonToStringStyler.java new file mode 100644 index 00000000..2fd04e73 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/JsonToStringStyler.java @@ -0,0 +1,163 @@ +/* + * Copyright 2020 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model; + +import java.util.List; +import java.util.Map; +import java.util.StringJoiner; + +import javax.annotation.Nullable; + +import org.springframework.core.style.ToStringStyler; +import org.springframework.core.style.ValueStyler; + +public class JsonToStringStyler implements ToStringStyler, ValueStyler{ + public static final ToStringStyler INSTANCE = new JsonToStringStyler(); + public static final String INDENT_SPACES = " "; + private int indent = 0; + + private void newLine(StringBuilder buffer) { + buffer.append('\n'); + buffer.append(getIndentSpaces(0)); + } + + private String getIndentSpaces(int indentOffset) { + StringBuilder sb = new StringBuilder(); + for (int i=0; i list) { + if (list.isEmpty()) { + return "[]"; + } + + StringJoiner result = new StringJoiner(",\n" + getIndentSpaces(1), "[\n" + getIndentSpaces(1) , "\n" + getIndentSpaces(0) + "]"); + indent++; + for (Object o : list) { + result.add(style(o)); + } + indent--; + return result.toString(); + } + + private String getMapStyle(Map map) { + if (map.isEmpty()) { + return "[]"; + } + + StringJoiner result = new StringJoiner(",\n" + getIndentSpaces(1), "[\n" + getIndentSpaces(1) , "\n" + getIndentSpaces(0) + "]"); + indent++; + for (Object o : map.values()) { + result.add(style(o)); + } + indent--; + return result.toString(); + } + + private String getDefaultStyle(Object value) { + return String.valueOf(value); + } + + @Override + public void styleStart(StringBuilder buffer, Object obj) { + indent++; + if (!obj.getClass().isArray()) { + buffer.append("{"); + newLine(buffer); + buffer.append("\"className\": "); + buffer.append('"'); + buffer.append(obj.getClass().getSimpleName()); + buffer.append('"'); + buffer.append(','); + } else { + buffer.append('['); + styleValue(buffer, obj); + } + } + + @Override + public void styleEnd(StringBuilder buffer, Object obj) { + indent--; + newLine(buffer); + if (!obj.getClass().isArray()) { + buffer.append('}'); + } else { + buffer.append(']'); + } + } + + @Override + public void styleField(StringBuilder buffer, String fieldName, @Nullable Object value) { + newLine(buffer); + buffer.append('"'); + buffer.append(fieldName); + buffer.append('"'); + buffer.append(": "); + styleValue(buffer, value); + } + + @Override + public void styleValue(StringBuilder buffer, Object value) { + buffer.append(style(value)); + } + + @Override + public void styleFieldSeparator(StringBuilder buffer) { + buffer.append(","); + } + + @Override + public String style(Object value) { + if (value == null) { + return "null"; + } else if (value instanceof String) { + return getStringStyle((String) value); + } else if (value instanceof Object[]) { + return getArrayStyle((Object[]) value); + } else if (value instanceof List) { + return getListStyle((List) value); + } else if (value instanceof Map) { + return getMapStyle((Map) value); + } else { + return getDefaultStyle(value); + } + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/TestRealtimerReporterEventTimedConsumer.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/LimitedLinkedHashMap.java similarity index 50% rename from sqldev/src/test/java/org/utplsql/sqldev/test/dal/TestRealtimerReporterEventTimedConsumer.xtend rename to sqldev/src/main/java/org/utplsql/sqldev/model/LimitedLinkedHashMap.java index ff46d749..de702252 100644 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/TestRealtimerReporterEventTimedConsumer.xtend +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/LimitedLinkedHashMap.java @@ -13,25 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.utplsql.sqldev.test.dal +package org.utplsql.sqldev.model; -import java.util.HashMap -import org.utplsql.sqldev.dal.RealtimeReporterEventConsumer -import org.utplsql.sqldev.model.runner.RealtimeReporterEvent -import org.utplsql.sqldev.model.runner.PostTestEvent +import java.util.LinkedHashMap; +import java.util.Map; -class TestRealtimerReporterEventTimedConsumer implements RealtimeReporterEventConsumer { - - val postTestEvents = new HashMap - - def getPostTestEvents() { - return postTestEvents - } - - override void process(RealtimeReporterEvent event) { - if (event instanceof PostTestEvent) { - postTestEvents.put(event.id, System.currentTimeMillis) - } - } +public class LimitedLinkedHashMap extends LinkedHashMap { + private static final long serialVersionUID = -4184317926729190411L; + private final int maxEntries; -} \ No newline at end of file + public LimitedLinkedHashMap(final int maxEntries) { + super((maxEntries + 1), 1.0f, false); + this.maxEntries = maxEntries; + } + + @Override + public boolean removeEldestEntry(final Map.Entry eldest) { + return (size() > maxEntries); + } + + public int getMaxEntries() { + return maxEntries; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/LimitedLinkedHashMap.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/LimitedLinkedHashMap.xtend deleted file mode 100644 index 2d3605d9..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/LimitedLinkedHashMap.xtend +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model - -import java.util.LinkedHashMap -import java.util.Map - -class LimitedLinkedHashMap extends LinkedHashMap { - val int maxEntries - - new (int maxEntries) { - super(maxEntries + 1, 1.0f, false) - - this.maxEntries = maxEntries; - } - - override removeEldestEntry(Map.Entry eldest) { - return size > maxEntries - } - - def getMaxEntries() { - return maxEntries - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/PrefixTools.java b/sqldev/src/main/java/org/utplsql/sqldev/model/PrefixTools.java new file mode 100644 index 00000000..e17ce69d --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/PrefixTools.java @@ -0,0 +1,88 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model; + +import java.util.List; + +//converted to Xtend based on Java code on https://www.geeksforgeeks.org/longest-common-prefix-using-binary-search/ +//converted back to Java with some amendments +public class PrefixTools { + + // do not instantiate this class + private PrefixTools() { + super(); + } + + public static int findMinLength(final String[] arr, final int n) { + int min = Integer.MAX_VALUE; + for (int i=0; i < n; i++) { + if (arr[i].length() < min) { + min = arr[i].length(); + } + } + return min; + } + + public static boolean allContainsPrefix(final String[] arr, final int n, final String str, final int start, final int end) { + for (int i=0; i < n; i++) { + String item = arr[i]; + for (int j = start; j <= end; j++) { + if (item.charAt(j) != str.charAt(j)) { + return false; + } + } + } + return true; + } + + public static String commonPrefix(final String[] arr, final int n) { + int index = findMinLength(arr, n); + StringBuilder prefix = new StringBuilder(); + int low = 0; + int high = index; // index-1 is wrong + while (low <= high) { + int mid = low + (high - low) / 2; + if (allContainsPrefix(arr, n, arr[0], low, mid)) { + prefix.append(arr[0].substring(low, mid + 1)); + low = mid + 1; + } else { + high = mid - 1; + } + } + return prefix.toString(); + } + + public static String commonPrefix(final List list) { + try { + if (list.isEmpty()) { + return ""; + } else if (list.size() == 1) { + final int pos = list.get(0).lastIndexOf('.'); + if (pos > 0) { + return list.get(0).substring(0, pos + 1); + } else { + return ""; + } + } else { + final String[] testArray = new String[list.size()]; + return commonPrefix(list.toArray(testArray), list.size()); + } + + } catch (Exception e) { + return ""; + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/PrefixTools.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/PrefixTools.xtend deleted file mode 100644 index c1fc822f..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/PrefixTools.xtend +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model - -import java.util.List - -// converted to Xtend based on Java code on https://www.geeksforgeeks.org/longest-common-prefix-using-binary-search/ -class PrefixTools { - def static int findMinLength(String[] arr, int n) { - var int min = Integer.MAX_VALUE - for (var int i = 0; i < n; i++) { - if ({ - val _rdIndx_arr = i - arr.get(_rdIndx_arr) - }.length() < min) { - min = { - val _rdIndx_arr = i - arr.get(_rdIndx_arr) - }.length() - } - } - return min - } - - def static boolean allContainsPrefix(String[] arr, int n, String str, int start, int end) { - for (var int i = 0; i < n; i++) { - var String arr_i = { - val _rdIndx_arr = i - arr.get(_rdIndx_arr) - } - for (var int j = start; j <= end; j++) { - if (arr_i.charAt(j) !== str.charAt(j)) { - return false - } - } - } - return true - } - - def static String commonPrefix(String[] arr, int n) { - var int index = findMinLength(arr, n) - var String prefix = "" - var int low = 0 - var int high = index - while (low <= high) { - var int mid = low + (high - low) / 2 - if (allContainsPrefix(arr, n, arr.get(0), low, mid)) { - prefix = prefix + arr.get(0).substring(low, mid + 1) - low = mid + 1 - } else { - high = mid - 1 - } - } - return prefix - } - - def static String commonPrefix(List list) { - try { - if (list.size === 0) { - return "" - } else if (list.size === 1) { - val pos = list.get(0).lastIndexOf("."); - if (pos > 0) { - return list.get(0).substring(0, pos + 1) - } else { - return "" - } - } else { - var String[] testArray = newArrayOfSize(list.size) - var prefix = commonPrefix(list.toArray(testArray), list.size) - return prefix - } - } catch (Exception e) { - return "" - } - } - -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/StringTools.java b/sqldev/src/main/java/org/utplsql/sqldev/model/StringTools.java new file mode 100644 index 00000000..23fc20f9 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/StringTools.java @@ -0,0 +1,77 @@ +/* + * Copyright 2020 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model; + +import java.util.Collections; +import java.util.List; + +public class StringTools { + // do not instantiate this class + private StringTools() { + super(); + } + + public static String getCSV(List list, String indent) { + final StringBuilder sb = new StringBuilder(); + for (final String item : list) { + if (sb.length() > 0) { + sb.append(",\n"); + } + sb.append(indent); + sb.append("'"); + sb.append(item); + sb.append("'"); + } + sb.append("\n"); + return sb.toString(); + } + + public static String getCSV(List list, int indentSpaces) { + return getCSV(list, repeat(" ", indentSpaces)); + } + + public static String getSimpleCSV(List list) { + final StringBuilder sb = new StringBuilder(); + for (final String item : list) { + if (sb.length() > 0) { + sb.append(", "); + } + sb.append(item); + } + return sb.toString(); + } + + public static String repeat(String s, int times) { + return String.join("", Collections.nCopies(times, s)); + } + + public static String replaceTabsWithSpaces(final CharSequence input, int indentSpaces) { + final String spaces = StringTools.repeat(" ", indentSpaces); + return input.toString().replace("\t", spaces); + } + + public static String formatDateTime(final String dateTime) { + if (dateTime == null) { + return null; + } else { + if (dateTime.length() == 26) { + return dateTime.replace("T", " ").substring(0, 23); + } else { + return dateTime; + } + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/SystemTools.java b/sqldev/src/main/java/org/utplsql/sqldev/model/SystemTools.java new file mode 100644 index 00000000..30a75172 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/SystemTools.java @@ -0,0 +1,39 @@ +/* + * Copyright 2020 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model; + +public class SystemTools { + // do not instantiate this class + private SystemTools() { + super(); + } + + public static void sleep(int millis) { + try { + Thread.sleep(millis); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } + + public static void waitForThread(Thread thread, int maxTimeInMillis) { + try { + thread.join(maxTimeInMillis); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/URLTools.java b/sqldev/src/main/java/org/utplsql/sqldev/model/URLTools.java new file mode 100644 index 00000000..de225bcf --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/URLTools.java @@ -0,0 +1,81 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model; + +import java.net.URL; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class URLTools { + + // do not instantiate this class + private URLTools() { + super(); + } + + public static String replaceHexChars(final String input) { + String output = input; + final Pattern p = Pattern.compile("%([0-9A-F]{2})"); + final Matcher m = p.matcher(input); + while (m.find()) { + final String what = m.group(0); + final int decimal = Integer.parseInt(m.group(1), 16); + final String with = String.valueOf((char) decimal); + output = output.replace(what, with); + } + return output; + } + + public static String getConnectionName(final URL url) { + final Pattern p = Pattern.compile("(sqldev.nav:)([^/]+)(//)?"); + final Matcher m = p.matcher(url.toString()); + if (m.find()) { + return replaceHexChars(m.group(2).replace("IdeConnections%2523", "IdeConnections%23")); + } else { + return ""; + } + } + + public static String getSchema(final URL url) { + final Pattern p = Pattern.compile("(//)([^/]+)"); + final Matcher m = p.matcher(url.toString()); + if (m.find()) { + return m.group(2); + } else { + return ""; + } + } + + public static String getObjectType(final URL url) { + final Pattern p = Pattern.compile("(//)([^/]+)(/)([^/]+)"); + final Matcher m = p.matcher(url.toString()); + if (m.find()) { + return m.group(4); + } else { + return ""; + } + } + + public static String getMemberObject(final URL url) { + final Pattern p = Pattern.compile("(/)([^/]+)(#MEMBER)"); + final Matcher m = p.matcher(url.toString()); + if (m.find()) { + return m.group(2); + } else { + return ""; + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/URLTools.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/URLTools.xtend deleted file mode 100644 index 507d7035..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/URLTools.xtend +++ /dev/null @@ -1,74 +0,0 @@ -/* Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model - -import java.net.URL -import java.util.regex.Pattern - -class URLTools { - def replaceHexChars(String input) { - var String output = input; - val p = Pattern.compile("%([0-9A-F]{2})") - val m = p.matcher(input) - while (m.find) { - val what = m.group(0); - val decimal = Integer.parseInt(m.group(1), 16) - val with = String.valueOf(decimal as char) - output = output.replace(what, with) - } - return output - } - - def getConnectionName(URL url) { - val p = Pattern.compile("(sqldev.nav:)([^/]+)(//)?") - val m = p.matcher(url.toString) - if (m.find) { - return m.group(2).replace("IdeConnections%2523", "IdeConnections%23").replaceHexChars - } else { - return "" - } - } - - def getSchema(URL url) { - val p = Pattern.compile("(//)([^/]+)") - val m = p.matcher(url.toString) - if (m.find) { - return m.group(2) - } else { - return "" - } - } - - def getObjectType(URL url) { - val p = Pattern.compile("(//)([^/]+)(/)([^/]+)") - val m = p.matcher(url.toString) - if (m.find) { - return m.group(4) - } else { - return "" - } - } - - def getMemberObject(URL url) { - val p = Pattern.compile("(/)([^/]+)(#MEMBER)") - val m = p.matcher(url.toString) - - if (m.find) { - return m.group(2) - } else { - return "" - } - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/XMLTools.java b/sqldev/src/main/java/org/utplsql/sqldev/model/XMLTools.java new file mode 100644 index 00000000..b9f2b982 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/XMLTools.java @@ -0,0 +1,161 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model; + +import java.io.IOException; +import java.io.StringWriter; +import java.util.logging.Logger; + +import javax.xml.XMLConstants; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.transform.OutputKeys; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import javax.xml.xpath.XPath; +import javax.xml.xpath.XPathConstants; +import javax.xml.xpath.XPathExpression; +import javax.xml.xpath.XPathExpressionException; +import javax.xml.xpath.XPathFactory; + +import org.utplsql.sqldev.exception.GenericRuntimeException; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; + +public class XMLTools { + private static final Logger logger = Logger.getLogger(XMLTools.class.getName()); + private final XPathFactory xpathFactory = XPathFactory.newInstance(); + private final XPath xpath = xpathFactory.newXPath(); + + public NodeList getNodeList(final Node doc, final String xpathString) { + try { + final XPathExpression expr = xpath.compile(xpathString); + return ((NodeList) expr.evaluate(doc, XPathConstants.NODESET)); + } catch (XPathExpressionException e) { + final String msg = "XPathExpressionException for " + xpathString + "."; + logger.severe(() -> msg); + throw new GenericRuntimeException(msg, e); + } + } + + public Node getNode(final Node doc, final String xpathString) { + try { + final XPathExpression expr = xpath.compile(xpathString); + return ((Node) expr.evaluate(doc, XPathConstants.NODE)); + } catch (XPathExpressionException e) { + final String msg = "XPathExpressionException for " + xpathString + "."; + logger.severe(() -> msg); + throw new GenericRuntimeException(msg, e); + } + } + + public void trimWhitespace(final Node node) { + final NodeList children = node.getChildNodes(); + for (int i = 0; i < children.getLength(); i++) { + final Node child = children.item(i); + if (child.getNodeType() == Node.TEXT_NODE) { + child.setTextContent(child.getTextContent().trim()); + } + trimWhitespace(child); + } + } + + public String nodeToString(final Node node, final String cdataSectionElements) { + try { + trimWhitespace(node); + final StringWriter writer = new StringWriter(); + TransformerFactory factory = TransformerFactory.newInstance(); + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true); + final Transformer transformer = factory.newTransformer(); + transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes"); + transformer.setOutputProperty(OutputKeys.INDENT, "yes"); + transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3"); + transformer.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, cdataSectionElements); + transformer.transform( new DOMSource(node), new StreamResult(writer)); + final String result = writer.toString(); + return result.replaceAll("", ""); + } catch (TransformerException e) { + final String msg = "TransformerException for " + cdataSectionElements + "."; + logger.severe(() -> msg); + throw new GenericRuntimeException(msg, e); + } + } + + public String getAttributeValue(final Node node, final String namedItem) { + String value = null; + if (node instanceof Element) { + final NamedNodeMap attributes = ((Element) node).getAttributes(); + if (attributes != null) { + final Node item = attributes.getNamedItem(namedItem); + if (item != null) { + value = item.getNodeValue(); + } + } + } + return value; + } + + public String getElementValue(final Node node, final String tagName) { + String value = null; + final Node item = getElementNode(node, tagName); + if (item != null) { + value = item.getTextContent(); + } + return value; + } + + public Node getElementNode(final Node node, final String tagName) { + Node resultNode = null; + if (node instanceof Element) { + NodeList list = ((Element) node).getElementsByTagName(tagName); + if (list != null && list.getLength() > 0) { + resultNode = list.item(0); + } + } + return resultNode; + } + + public DocumentBuilder createDocumentBuilder() { + DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); + try { + factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, Boolean.TRUE); + return factory.newDocumentBuilder(); + } catch (ParserConfigurationException e) { + final String msg = "Could not create no document builder."; + logger.severe(() -> msg); + throw new GenericRuntimeException(msg, e); + } + } + + public Document parse(final DocumentBuilder builder, final InputSource inputSource) { + try { + return builder.parse(inputSource); + } catch (SAXException | IOException e) { + final String msg = "Could not parse XML input."; + logger.severe(() -> msg); + throw new GenericRuntimeException(msg, e); + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/XMLTools.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/XMLTools.xtend deleted file mode 100644 index 5126486a..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/XMLTools.xtend +++ /dev/null @@ -1,67 +0,0 @@ -/* Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model - -import java.io.StringWriter -import javax.xml.transform.OutputKeys -import javax.xml.transform.TransformerFactory -import javax.xml.transform.dom.DOMSource -import javax.xml.transform.stream.StreamResult -import javax.xml.xpath.XPathConstants -import javax.xml.xpath.XPathFactory -import org.w3c.dom.Node -import org.w3c.dom.NodeList - -class XMLTools { - val xpathFactory = XPathFactory.newInstance() - val xpath = xpathFactory.newXPath() - - def getNodeList(Node doc, String xpathString) { - val expr = xpath.compile(xpathString); - val NodeList nodeList = expr.evaluate(doc, XPathConstants.NODESET) as NodeList - return nodeList - } - - def getNode(Node doc, String xpathString) { - val expr = xpath.compile(xpathString); - val Node node = expr.evaluate(doc, XPathConstants.NODE) as Node - return node - } - - def void trimWhitespace(Node node) { - val children = node.childNodes - for (i : 0 ..< children.length) { - val child = children.item(i) - if (child.nodeType == Node.TEXT_NODE) { - child.textContent = child.textContent.trim - } - trimWhitespace(child); - } - } - - def nodeToString(Node node, String cdataSectionElements) { - node.trimWhitespace - val writer = new StringWriter() - val factory = TransformerFactory.newInstance().newTransformer() - factory.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes") - factory.setOutputProperty(OutputKeys.INDENT, "yes") - factory.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "3"); - factory.setOutputProperty(OutputKeys.CDATA_SECTION_ELEMENTS, cdataSectionElements) - factory.transform(new DOMSource(node), new StreamResult(writer)) - val result = writer.toString() - val fixedResult = result.replaceAll('''''',"") - return fixedResult - } -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/oddgen/GenContext.java b/sqldev/src/main/java/org/utplsql/sqldev/model/oddgen/GenContext.java new file mode 100644 index 00000000..30fdfbf9 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/oddgen/GenContext.java @@ -0,0 +1,150 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.oddgen; + +import java.sql.Connection; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class GenContext { + private Connection conn; + private String objectType; + private String objectName; + private String testPackagePrefix; + private String testPackageSuffix; + private String testUnitPrefix; + private String testUnitSuffix; + private int numberOfTestsPerUnit; + private boolean generateComments; + private boolean disableTests; + private String suitePath; + private int indentSpaces; + + public Connection getConn() { + return conn; + } + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("conn", conn) + .append("objectType", objectType) + .append("objectName", objectName) + .append("testPackagePrefix", testPackagePrefix) + .append("testPackageSuffix", testPackageSuffix) + .append("testUnitPrefix", testUnitPrefix) + .append("testUnitSuffix", testUnitSuffix) + .append("numberOfTestsPerUnit", numberOfTestsPerUnit) + .append("generateComments", generateComments) + .append("disableTests", disableTests) + .append("suitePath", suitePath) + .append("indentSpaces", indentSpaces) + .toString(); + } + + public void setConn(final Connection conn) { + this.conn = conn; + } + + public String getObjectType() { + return objectType; + } + + public void setObjectType(final String objectType) { + this.objectType = objectType; + } + + public String getObjectName() { + return objectName; + } + + public void setObjectName(final String objectName) { + this.objectName = objectName; + } + + public String getTestPackagePrefix() { + return testPackagePrefix; + } + + public void setTestPackagePrefix(final String testPackagePrefix) { + this.testPackagePrefix = testPackagePrefix; + } + + public String getTestPackageSuffix() { + return testPackageSuffix; + } + + public void setTestPackageSuffix(final String testPackageSuffix) { + this.testPackageSuffix = testPackageSuffix; + } + + public String getTestUnitPrefix() { + return testUnitPrefix; + } + + public void setTestUnitPrefix(final String testUnitPrefix) { + this.testUnitPrefix = testUnitPrefix; + } + + public String getTestUnitSuffix() { + return testUnitSuffix; + } + + public void setTestUnitSuffix(final String testUnitSuffix) { + this.testUnitSuffix = testUnitSuffix; + } + + public int getNumberOfTestsPerUnit() { + return numberOfTestsPerUnit; + } + + public void setNumberOfTestsPerUnit(final int numberOfTestsPerUnit) { + this.numberOfTestsPerUnit = numberOfTestsPerUnit; + } + + public boolean isGenerateComments() { + return generateComments; + } + + public void setGenerateComments(final boolean generateComments) { + this.generateComments = generateComments; + } + + public boolean isDisableTests() { + return disableTests; + } + + public void setDisableTests(final boolean disableTests) { + this.disableTests = disableTests; + } + + public String getSuitePath() { + return suitePath; + } + + public void setSuitePath(final String suitePath) { + this.suitePath = suitePath; + } + + public int getIndentSpaces() { + return indentSpaces; + } + + public void setIndentSpaces(final int indentSpaces) { + this.indentSpaces = indentSpaces; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/parser/PlsqlObject.java b/sqldev/src/main/java/org/utplsql/sqldev/model/parser/PlsqlObject.java new file mode 100644 index 00000000..122cfc1c --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/parser/PlsqlObject.java @@ -0,0 +1,71 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.parser; + +import java.util.List; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; +import org.utplsql.sqldev.model.ut.Annotation; + +public class PlsqlObject { + private String name; + private String type; + private Integer position; + private List annotations; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("name", name) + .append("type", type) + .append("position", position) + .append("annotations", annotations) + .toString(); + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(final String type) { + this.type = type; + } + + public Integer getPosition() { + return position; + } + + public void setPosition(final Integer position) { + this.position = position; + } + + public List getAnnotations() { + return annotations; + } + + public void setAnnotations(final List annotations) { + this.annotations = annotations; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/parser/PlsqlObject.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/parser/PlsqlObject.xtend deleted file mode 100644 index 96b71db1..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/parser/PlsqlObject.xtend +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.parser - -import java.util.List -import org.eclipse.xtend.lib.annotations.Accessors -import org.utplsql.sqldev.model.AbstractModel -import org.utplsql.sqldev.model.ut.Annotation - -@Accessors -class PlsqlObject extends AbstractModel { - String name - String type - Integer position - List annotations -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/parser/Unit.java b/sqldev/src/main/java/org/utplsql/sqldev/model/parser/Unit.java new file mode 100644 index 00000000..990e877c --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/parser/Unit.java @@ -0,0 +1,58 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.parser; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class Unit { + private String name; + private Integer position; + private Integer positionOfName; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("name", name) + .append("position", position) + .append("positionOfName", positionOfName) + .toString(); + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public Integer getPosition() { + return position; + } + + public void setPosition(final Integer position) { + this.position = position; + } + + public Integer getPositionOfName() { + return positionOfName; + } + + public void setPositionOfName(final Integer positionOfName) { + this.positionOfName = positionOfName; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/parser/Unit.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/parser/Unit.xtend deleted file mode 100644 index a913f70b..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/parser/Unit.xtend +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.parser - -import org.eclipse.xtend.lib.annotations.Accessors -import org.utplsql.sqldev.model.AbstractModel - -@Accessors -class Unit extends AbstractModel { - String name - Integer position - Integer positionOfName -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/preference/PreferenceModel.java b/sqldev/src/main/java/org/utplsql/sqldev/model/preference/PreferenceModel.java new file mode 100644 index 00000000..693ace05 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/preference/PreferenceModel.java @@ -0,0 +1,357 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.preference; + +import java.io.File; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +import oracle.javatools.data.HashStructure; +import oracle.javatools.data.HashStructureAdapter; +import oracle.javatools.data.PropertyStorage; + +public class PreferenceModel extends HashStructureAdapter { + public static final String DEFAULT_OUTPUT_DIRECTORY = System.getProperty("user.home") + File.separator + "utplsql" + File.separator + "generated"; + private static final String DATA_KEY = "utplsql"; + + private PreferenceModel(final HashStructure hash) { + super(hash); + } + + public static PreferenceModel getInstance(final PropertyStorage prefs) { + return new PreferenceModel(findOrCreate(prefs, DATA_KEY)); + } + + private static final String KEY_USE_REALTIME_REPORTER = "useRealtimeRorter"; + private static final String KEY_UNSHARED_WORKSHEET = "unsharedWorksheet"; + private static final String KEY_RESET_PACKAGE = "resetPackage"; + private static final String KEY_CLEAR_SCREEN = "clearScreen"; + private static final String KEY_AUTO_EXECUTE = "autoExecute"; + private static final String KEY_CHECK_RUN_UTPLSQL_TEST = "checkRunUtplsqlTest"; + private static final String KEY_USE_SMART_TIMES = "useSmartTimes"; + private static final String KEY_NUMBER_OF_RUNS_IN_HISTORY = "numberOfRunsInHistory"; + private static final String KEY_SHOW_DISABLED_COUNTER = "showDisabledCounter"; + private static final String KEY_SHOW_WARNINGS_COUNTER = "showWarningsCounter"; + private static final String KEY_SHOW_INFO_COUNTER = "showInfoCounter"; + private static final String KEY_SHOW_WARNING_INDICATOR = "showWarningIndicator"; + private static final String KEY_SHOW_INFO_INDICATOR = "showInfoIndicator"; + private static final String KEY_SHOW_SUCCESSFUL_TESTS = "showSuccessfulTests"; + private static final String KEY_SHOW_DISABLED_TESTS = "showDisabledTests"; + private static final String KEY_SHOW_TEST_DESCRIPTION = "showTestDescription"; + private static final String KEY_SYNC_DETAIL_TAB = "syncDetailTab"; + private static final String KEY_TEST_PACKAGE_PREFIX = "testPackagePrefix"; + private static final String KEY_TEST_PACKAGE_SUFFIX = "testPackageSuffix"; + private static final String KEY_TEST_UNIT_PREFIX = "testUnitPrefix"; + private static final String KEY_TEST_UNIT_SUFFIX = "testUnitSuffix"; + private static final String KEY_NUMBER_OF_TESTS_PER_UNIT = "numberOfTestsPerUnit"; + private static final String KEY_CHECK_GENERATE_UTPLSQL_TEST = "checkGenerateUtplsqlTest"; + private static final String KEY_GENERATE_COMMENTS = "generateComments"; + private static final String KEY_DISABLE_TESTS = "disableTests"; + private static final String KEY_SUITE_PATH = "suitePath"; + private static final String KEY_INDENT_SPACES = "indentSpaces"; + private static final String KEY_GENERATE_FILES = "generateFiles"; + private static final String KEY_OUTPUT_DIRECTORY = "outputDirectory"; + private static final String KEY_DELETE_EXISTING_FILES = "deleteExistingFiles"; + private static final String KEY_ROOT_FOLDER_IN_ODDGEN_VIEW = "rootFolderInOddgenView"; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append(KEY_USE_REALTIME_REPORTER, isUseRealtimeReporter()) + .append(KEY_UNSHARED_WORKSHEET, isUnsharedWorksheet()) + .append(KEY_RESET_PACKAGE, isResetPackage()) + .append(KEY_CLEAR_SCREEN, isClearScreen()) + .append(KEY_AUTO_EXECUTE, isAutoExecute()) + .append(KEY_CHECK_RUN_UTPLSQL_TEST, isCheckRunUtplsqlTest()) + .append(KEY_USE_SMART_TIMES, isUseSmartTimes()) + .append(KEY_NUMBER_OF_RUNS_IN_HISTORY, getNumberOfRunsInHistory()) + .append(KEY_SHOW_DISABLED_COUNTER, isShowDisabledCounter()) + .append(KEY_SHOW_WARNINGS_COUNTER, isShowWarningsCounter()) + .append(KEY_SHOW_INFO_COUNTER, isShowInfoCounter()) + .append(KEY_SHOW_WARNING_INDICATOR, isShowWarningIndicator()) + .append(KEY_SHOW_INFO_INDICATOR, isShowInfoIndicator()) + .append(KEY_SHOW_SUCCESSFUL_TESTS, isShowSuccessfulTests()) + .append(KEY_SHOW_DISABLED_TESTS, isShowDisabledTests()) + .append(KEY_SHOW_TEST_DESCRIPTION, isShowTestDescription()) + .append(KEY_SYNC_DETAIL_TAB, isSyncDetailTab()) + .append(KEY_TEST_PACKAGE_PREFIX, getTestPackagePrefix()) + .append(KEY_TEST_PACKAGE_SUFFIX, getTestPackageSuffix()) + .append(KEY_TEST_UNIT_PREFIX, getTestUnitPrefix()) + .append(KEY_TEST_UNIT_SUFFIX, getTestUnitSuffix()) + .append(KEY_NUMBER_OF_TESTS_PER_UNIT, getNumberOfTestsPerUnit()) + .append(KEY_CHECK_GENERATE_UTPLSQL_TEST, isCheckGenerateUtplsqlTest()) + .append(KEY_GENERATE_COMMENTS, isGenerateComments()) + .append(KEY_DISABLE_TESTS, isDisableTests()) + .append(KEY_SUITE_PATH, getSuitePath()) + .append(KEY_INDENT_SPACES, getIndentSpaces()) + .append(KEY_GENERATE_FILES, isGenerateFiles()) + .append(KEY_OUTPUT_DIRECTORY, getOutputDirectory()) + .append(KEY_DELETE_EXISTING_FILES, isDeleteExistingFiles()) + .append(KEY_ROOT_FOLDER_IN_ODDGEN_VIEW, getRootFolderInOddgenView()) + .toString(); + } + + public boolean isUseRealtimeReporter() { + return getHashStructure().getBoolean(KEY_USE_REALTIME_REPORTER, true); + } + + public void setUseRealtimeReporter(final boolean useRealtimeReporter) { + getHashStructure().putBoolean(KEY_USE_REALTIME_REPORTER, useRealtimeReporter); + } + + public boolean isUnsharedWorksheet() { + return getHashStructure().getBoolean(KEY_UNSHARED_WORKSHEET, true); + } + + public void setUnsharedWorksheet(final boolean unsharedWorksheet) { + getHashStructure().putBoolean(KEY_UNSHARED_WORKSHEET, unsharedWorksheet); + } + + public boolean isResetPackage() { + return getHashStructure().getBoolean(KEY_RESET_PACKAGE, false); + } + + public void setResetPackage(final boolean resetPackage) { + getHashStructure().putBoolean(KEY_RESET_PACKAGE, resetPackage); + } + + public boolean isClearScreen() { + return getHashStructure().getBoolean(KEY_CLEAR_SCREEN, false); + } + + public void setClearScreen(final boolean clearScreen) { + getHashStructure().putBoolean(KEY_CLEAR_SCREEN, clearScreen); + } + + public boolean isAutoExecute() { + return getHashStructure().getBoolean(KEY_AUTO_EXECUTE, true); + } + + public void setAutoExecute(final boolean autoExecute) { + getHashStructure().putBoolean(KEY_AUTO_EXECUTE, autoExecute); + } + + public boolean isCheckRunUtplsqlTest() { + return getHashStructure().getBoolean(KEY_CHECK_RUN_UTPLSQL_TEST, false); + } + + public void setCheckRunUtplsqlTest(final boolean checkRunUtplsqlTest) { + getHashStructure().putBoolean(KEY_CHECK_RUN_UTPLSQL_TEST, checkRunUtplsqlTest); + } + + public boolean isUseSmartTimes() { + return getHashStructure().getBoolean(KEY_USE_SMART_TIMES, false); + } + + public void setUseSmartTimes(final boolean useSmartTimes) { + getHashStructure().putBoolean(KEY_USE_SMART_TIMES, useSmartTimes); + } + + public int getNumberOfRunsInHistory() { + return getHashStructure().getInt(KEY_NUMBER_OF_RUNS_IN_HISTORY, 10); + } + + public void setNumberOfRunsInHistory(final int runs) { + getHashStructure().putInt(KEY_NUMBER_OF_RUNS_IN_HISTORY, runs); + } + + public boolean isShowDisabledCounter() { + return getHashStructure().getBoolean(KEY_SHOW_DISABLED_COUNTER, false); + } + + public void setShowDisabledCounter(final boolean showDisabledCounter) { + getHashStructure().putBoolean(KEY_SHOW_DISABLED_COUNTER, showDisabledCounter); + } + + public boolean isShowWarningsCounter() { + return getHashStructure().getBoolean(KEY_SHOW_WARNINGS_COUNTER, false); + } + + public void setShowWarningsCounter(final boolean showWarningCounter) { + getHashStructure().putBoolean(KEY_SHOW_WARNINGS_COUNTER, showWarningCounter); + } + + public boolean isShowInfoCounter() { + return getHashStructure().getBoolean(KEY_SHOW_INFO_COUNTER, false); + } + + public void setShowInfoCounter(final boolean showInfoCounter) { + getHashStructure().putBoolean(KEY_SHOW_INFO_COUNTER, showInfoCounter); + } + + public boolean isShowWarningIndicator() { + return getHashStructure().getBoolean(KEY_SHOW_WARNING_INDICATOR, false); + } + + public void setShowWarningIndicator(final boolean showWarningIndicator) { + getHashStructure().putBoolean(KEY_SHOW_WARNING_INDICATOR, showWarningIndicator); + } + + public boolean isShowInfoIndicator() { + return getHashStructure().getBoolean(KEY_SHOW_INFO_INDICATOR, false); + } + + public void setShowInfoIndicator(final boolean showInfoIndicator) { + getHashStructure().putBoolean(KEY_SHOW_INFO_INDICATOR, showInfoIndicator); + } + + public boolean isShowSuccessfulTests() { + return getHashStructure().getBoolean(KEY_SHOW_SUCCESSFUL_TESTS, true); + } + + public void setShowSuccessfulTests(final boolean showSuccessfulTests) { + getHashStructure().putBoolean(KEY_SHOW_SUCCESSFUL_TESTS, showSuccessfulTests); + } + + public boolean isShowDisabledTests() { + return getHashStructure().getBoolean(KEY_SHOW_DISABLED_TESTS, true); + } + + public void setShowDisabledTests(final boolean showDisabledTests) { + getHashStructure().putBoolean(KEY_SHOW_DISABLED_TESTS, showDisabledTests); + } + + public boolean isShowTestDescription() { + return getHashStructure().getBoolean(KEY_SHOW_TEST_DESCRIPTION, false); + } + + public void setShowTestDescription(final boolean showTestDescription) { + getHashStructure().putBoolean(KEY_SHOW_TEST_DESCRIPTION, showTestDescription); + } + + public boolean isSyncDetailTab() { + return getHashStructure().getBoolean(KEY_SYNC_DETAIL_TAB, true); + } + + public void setSyncDetailTab(final boolean syncDetailTab) { + getHashStructure().putBoolean(KEY_SYNC_DETAIL_TAB, syncDetailTab); + } + + public String getTestPackagePrefix() { + return getHashStructure().getString(KEY_TEST_PACKAGE_PREFIX, "test_"); + } + + public void setTestPackagePrefix(final String testPackagePrefix) { + getHashStructure().putString(KEY_TEST_PACKAGE_PREFIX, testPackagePrefix); + } + + public String getTestPackageSuffix() { + return getHashStructure().getString(KEY_TEST_PACKAGE_SUFFIX, ""); + } + + public void setTestPackageSuffix(final String testPackageSuffix) { + getHashStructure().putString(KEY_TEST_PACKAGE_SUFFIX, testPackageSuffix); + } + + public String getTestUnitPrefix() { + return getHashStructure().getString(KEY_TEST_UNIT_PREFIX, ""); + } + + public void setTestUnitPrefix(final String testUnitPrefix) { + getHashStructure().putString(KEY_TEST_UNIT_PREFIX, testUnitPrefix); + } + + public String getTestUnitSuffix() { + return getHashStructure().getString(KEY_TEST_UNIT_SUFFIX, ""); + } + + public void setTestUnitSuffix(final String testUnitSuffix) { + getHashStructure().putString(KEY_TEST_UNIT_SUFFIX, testUnitSuffix); + } + + public int getNumberOfTestsPerUnit() { + return getHashStructure().getInt(KEY_NUMBER_OF_TESTS_PER_UNIT, 1); + } + + public void setNumberOfTestsPerUnit(final int numberOfTestsPerUnit) { + getHashStructure().putInt(KEY_NUMBER_OF_TESTS_PER_UNIT, numberOfTestsPerUnit); + } + + public boolean isCheckGenerateUtplsqlTest() { + return getHashStructure().getBoolean(KEY_CHECK_GENERATE_UTPLSQL_TEST, false); + } + + public void setCheckGenerateUtplsqlTest(final boolean checkGenerateUtplsqlTest) { + getHashStructure().putBoolean(KEY_CHECK_GENERATE_UTPLSQL_TEST, checkGenerateUtplsqlTest); + } + + public boolean isGenerateComments() { + return getHashStructure().getBoolean(KEY_GENERATE_COMMENTS, true); + } + + public void setGenerateComments(final boolean generateComments) { + getHashStructure().putBoolean(KEY_GENERATE_COMMENTS, generateComments); + } + + public boolean isDisableTests() { + return getHashStructure().getBoolean(KEY_DISABLE_TESTS, false); + } + + public void setDisableTests(final boolean disableTests) { + getHashStructure().putBoolean(KEY_DISABLE_TESTS, disableTests); + } + + public String getSuitePath() { + return getHashStructure().getString(KEY_SUITE_PATH, "alltests"); + } + + public void setSuitePath(final String suitePath) { + getHashStructure().putString(KEY_SUITE_PATH, suitePath); + } + + public int getIndentSpaces() { + return getHashStructure().getInt(KEY_INDENT_SPACES, 3); + } + + public void setIndentSpaces(final int indentSpaces) { + getHashStructure().putInt(KEY_INDENT_SPACES, indentSpaces); + } + + public boolean isGenerateFiles() { + return getHashStructure().getBoolean(KEY_GENERATE_FILES, true); + } + + public void setGenerateFiles(final boolean generateFiles) { + getHashStructure().putBoolean(KEY_GENERATE_FILES, generateFiles); + } + + public String getOutputDirectory() { + return getHashStructure().getString(KEY_OUTPUT_DIRECTORY, DEFAULT_OUTPUT_DIRECTORY); + } + + public void setOutputDirectory(final String outputDirectory) { + final String dir = outputDirectory.isEmpty() ? DEFAULT_OUTPUT_DIRECTORY : outputDirectory; + getHashStructure().putString(KEY_OUTPUT_DIRECTORY, dir); + } + + public boolean isDeleteExistingFiles() { + return getHashStructure().getBoolean(KEY_DELETE_EXISTING_FILES, false); + } + + public void setDeleteExistingFiles(final boolean deleteExistingFiles) { + getHashStructure().putBoolean(KEY_DELETE_EXISTING_FILES, deleteExistingFiles); + } + + public String getRootFolderInOddgenView() { + return getHashStructure().getString(KEY_ROOT_FOLDER_IN_ODDGEN_VIEW, "utPLSQL"); + } + + public void setRootFolderInOddgenView(final String rootFolder) { + final String folder = rootFolder.isEmpty() ? "utPLSQL" : rootFolder; + getHashStructure().putString(KEY_ROOT_FOLDER_IN_ODDGEN_VIEW, folder); + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/preference/PreferenceModel.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/preference/PreferenceModel.xtend deleted file mode 100644 index 6ae9ea80..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/preference/PreferenceModel.xtend +++ /dev/null @@ -1,321 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.preference - -import java.io.File -import oracle.javatools.data.HashStructure -import oracle.javatools.data.HashStructureAdapter -import oracle.javatools.data.PropertyStorage -import org.eclipse.xtext.xbase.lib.util.ToStringBuilder - -class PreferenceModel extends HashStructureAdapter { - public static final String DEFAULT_OUTPUT_DIRECTORY = '''«System.getProperty("user.home")»«File.separator»utplsql«File.separator»generated''' - static final String DATA_KEY = "utplsql" - - private new(HashStructure hash) { - super(hash) - } - - def static getInstance(PropertyStorage prefs) { - return new PreferenceModel(findOrCreate(prefs, DATA_KEY)) - } - - static final String KEY_USE_REALTIME_REPORTER = "useRealtimeRorter" - static final String KEY_UNSHARED_WORKSHEET = "unsharedWorksheet" - static final String KEY_RESET_PACKAGE = "resetPackage" - static final String KEY_CLEAR_SCREEN = "clearScreen" - static final String KEY_AUTO_EXECUTE = "autoExecute" - static final String KEY_CHECK_RUN_UTPLSQL_TEST = "checkRunUtplsqlTest" - static final String KEY_USE_SMART_TIMES = "useSmartTimes" - static final String KEY_NUMBER_OF_RUNS_IN_HISTORY = "numberOfRunsInHistory" - static final String KEY_SHOW_DISABLED_COUNTER = "showDisabledCounter" - static final String KEY_SHOW_WARNINGS_COUNTER = "showWarningsCounter" - static final String KEY_SHOW_INFO_COUNTER = "showInfoCounter" - static final String KEY_SHOW_WARNING_INDICATOR = "showWarningIndicator" - static final String KEY_SHOW_INFO_INDICATOR = "showInfoIndicator" - static final String KEY_SHOW_SUCCESSFUL_TESTS = "showSuccessfulTests" - static final String KEY_SHOW_DISABLED_TESTS = "showDisabledTests" - static final String KEY_SHOW_TEST_DESCRIPTION = "showTestDescription" - static final String KEY_SYNC_DETAIL_TAB = "syncDetailTab" - static final String KEY_TEST_PACKAGE_PREFIX = "testPackagePrefix" - static final String KEY_TEST_PACKAGE_SUFFIX = "testPackageSuffix" - static final String KEY_TEST_UNIT_PREFIX = "testUnitPrefix" - static final String KEY_TEST_UNIT_SUFFIX = "testUnitSuffix" - static final String KEY_NUMBER_OF_TESTS_PER_UNIT = "numberOfTestsPerUnit" - static final String KEY_CHECK_GENERATE_UTPLSQL_TEST = "checkGenerateUtplsqlTest" - static final String KEY_GENERATE_COMMENTS = "generateComments" - static final String KEY_DISABLE_TESTS = "disableTests" - static final String KEY_SUITE_PATH="suitePath" - static final String KEY_INDENT_SPACES="indentSpaces" - static final String KEY_GENERATE_FILES="generateFiles" - static final String KEY_OUTPUT_DIRECTORY = "outputDirectory" - static final String KEY_DELETE_EXISTING_FILES="deleteExistingFiles" - static final String KEY_ROOT_FOLDER_IN_ODDGEN_VIEW = "rootFolderInOddgenView" - - def isUseRealtimeReporter() { - return getHashStructure.getBoolean(PreferenceModel.KEY_USE_REALTIME_REPORTER, true) - } - - def setUseRealtimeReporter(boolean useRealtimeReporter) { - getHashStructure.putBoolean(PreferenceModel.KEY_USE_REALTIME_REPORTER, useRealtimeReporter) - } - - def isUnsharedWorksheet() { - return getHashStructure.getBoolean(PreferenceModel.KEY_UNSHARED_WORKSHEET, true) - } - - def setUnsharedWorksheet(boolean unsharedWorksheet) { - getHashStructure.putBoolean(PreferenceModel.KEY_UNSHARED_WORKSHEET, unsharedWorksheet) - } - - def isResetPackage() { - return getHashStructure.getBoolean(PreferenceModel.KEY_RESET_PACKAGE, false) - } - - def setResetPackage(boolean resetPackage) { - getHashStructure.putBoolean(PreferenceModel.KEY_RESET_PACKAGE, resetPackage) - } - - def isClearScreen() { - return getHashStructure.getBoolean(PreferenceModel.KEY_CLEAR_SCREEN, false) - } - - def setClearScreen(boolean clearScreen) { - getHashStructure.putBoolean(PreferenceModel.KEY_CLEAR_SCREEN, clearScreen) - } - - def isAutoExecute() { - return getHashStructure.getBoolean(PreferenceModel.KEY_AUTO_EXECUTE, true) - } - - def setAutoExecute(boolean autoExecute) { - getHashStructure.putBoolean(PreferenceModel.KEY_AUTO_EXECUTE, autoExecute) - } - - def isCheckRunUtplsqlTest() { - return getHashStructure.getBoolean(PreferenceModel.KEY_CHECK_RUN_UTPLSQL_TEST, false) - } - - def setCheckRunUtplsqlTest(boolean checkRunUtplsqlTest) { - getHashStructure.putBoolean(PreferenceModel.KEY_CHECK_RUN_UTPLSQL_TEST, checkRunUtplsqlTest) - } - - def isUseSmartTimes() { - return getHashStructure.getBoolean(PreferenceModel.KEY_USE_SMART_TIMES, false) - } - - def setUseSmartTimes(boolean useSmartTimes) { - getHashStructure.putBoolean(PreferenceModel.KEY_USE_SMART_TIMES, useSmartTimes) - } - - def getNumberOfRunsInHistory() { - return getHashStructure.getInt(PreferenceModel.KEY_NUMBER_OF_RUNS_IN_HISTORY, 10) - } - - def setNumberOfRunsInHistory(int runs) { - getHashStructure.putInt(PreferenceModel.KEY_NUMBER_OF_RUNS_IN_HISTORY, runs) - } - - def isShowDisabledCounter() { - return getHashStructure.getBoolean(PreferenceModel.KEY_SHOW_DISABLED_COUNTER, false) - } - - def setShowDisabledCounter(boolean showDisabledCounter) { - getHashStructure.putBoolean(PreferenceModel.KEY_SHOW_DISABLED_COUNTER, showDisabledCounter) - } - - def isShowWarningsCounter() { - return getHashStructure.getBoolean(PreferenceModel.KEY_SHOW_WARNINGS_COUNTER, false) - } - - def setShowWarningsCounter(boolean showWarningCounter) { - getHashStructure.putBoolean(PreferenceModel.KEY_SHOW_WARNINGS_COUNTER, showWarningCounter) - } - - def isShowInfoCounter() { - return getHashStructure.getBoolean(PreferenceModel.KEY_SHOW_INFO_COUNTER, false) - } - - def setShowInfoCounter(boolean showInfoCounter) { - getHashStructure.putBoolean(PreferenceModel.KEY_SHOW_INFO_COUNTER, showInfoCounter) - } - - def isShowWarningIndicator() { - return getHashStructure.getBoolean(PreferenceModel.KEY_SHOW_WARNING_INDICATOR, false) - } - - def setShowWarningIndicator(boolean showWarningIndicator) { - getHashStructure.putBoolean(PreferenceModel.KEY_SHOW_WARNING_INDICATOR, showWarningIndicator) - } - - def isShowInfoIndicator() { - return getHashStructure.getBoolean(PreferenceModel.KEY_SHOW_INFO_INDICATOR, false) - } - - def setShowInfoIndicator(boolean showInfoIndicator) { - getHashStructure.putBoolean(PreferenceModel.KEY_SHOW_INFO_INDICATOR, showInfoIndicator) - } - - def isShowSuccessfulTests() { - return getHashStructure.getBoolean(PreferenceModel.KEY_SHOW_SUCCESSFUL_TESTS, true) - } - - def setShowSuccessfulTests(boolean showSuccessfulTests) { - getHashStructure.putBoolean(PreferenceModel.KEY_SHOW_SUCCESSFUL_TESTS, showSuccessfulTests) - } - - def isShowDisabledTests() { - return getHashStructure.getBoolean(PreferenceModel.KEY_SHOW_DISABLED_TESTS, true) - } - - def setShowDisabledTests(boolean showDisabledTests) { - getHashStructure.putBoolean(PreferenceModel.KEY_SHOW_DISABLED_TESTS, showDisabledTests) - } - - def isShowTestDescription() { - return getHashStructure.getBoolean(PreferenceModel.KEY_SHOW_TEST_DESCRIPTION, false) - } - - def setShowTestDescription(boolean showTestDescription) { - getHashStructure.putBoolean(PreferenceModel.KEY_SHOW_TEST_DESCRIPTION, showTestDescription) - } - - def isSyncDetailTab() { - return getHashStructure.getBoolean(PreferenceModel.KEY_SYNC_DETAIL_TAB, true) - } - - def setSyncDetailTab(boolean syncDetailTab) { - getHashStructure.putBoolean(PreferenceModel.KEY_SYNC_DETAIL_TAB, syncDetailTab) - } - - def getTestPackagePrefix() { - return getHashStructure.getString(PreferenceModel.KEY_TEST_PACKAGE_PREFIX, "test_") - } - - def setTestPackagePrefix(String testPackagePrefix) { - getHashStructure.putString(PreferenceModel.KEY_TEST_PACKAGE_PREFIX, testPackagePrefix) - } - - def getTestPackageSuffix() { - return getHashStructure.getString(PreferenceModel.KEY_TEST_PACKAGE_SUFFIX, "") - } - - def setTestPackageSuffix(String testPackageSuffix) { - getHashStructure.putString(PreferenceModel.KEY_TEST_PACKAGE_SUFFIX, testPackageSuffix) - } - - def getTestUnitPrefix() { - return getHashStructure.getString(PreferenceModel.KEY_TEST_UNIT_PREFIX, "") - } - - def setTestUnitPrefix(String testUnitPrefix) { - getHashStructure.putString(PreferenceModel.KEY_TEST_UNIT_PREFIX, testUnitPrefix) - } - - def getTestUnitSuffix() { - return getHashStructure.getString(PreferenceModel.KEY_TEST_UNIT_SUFFIX, "") - } - - def setTestUnitSuffix(String testUnitSuffix) { - getHashStructure.putString(PreferenceModel.KEY_TEST_UNIT_SUFFIX, testUnitSuffix) - } - - def getNumberOfTestsPerUnit() { - return getHashStructure.getInt(PreferenceModel.KEY_NUMBER_OF_TESTS_PER_UNIT, 1) - } - - def setNumberOfTestsPerUnit(int numberOfTestsPerUnit) { - getHashStructure.putInt(PreferenceModel.KEY_NUMBER_OF_TESTS_PER_UNIT, numberOfTestsPerUnit) - } - - def isCheckGenerateUtplsqlTest() { - return getHashStructure.getBoolean(PreferenceModel.KEY_CHECK_GENERATE_UTPLSQL_TEST, false) - } - - def setCheckGenerateUtplsqlTest(boolean checkGenerateUtplsqlTest) { - getHashStructure.putBoolean(PreferenceModel.KEY_CHECK_GENERATE_UTPLSQL_TEST, checkGenerateUtplsqlTest) - } - - def isGenerateComments() { - return getHashStructure.getBoolean(PreferenceModel.KEY_GENERATE_COMMENTS, true) - } - - def setGenerateComments(boolean generateComments) { - getHashStructure.putBoolean(PreferenceModel.KEY_GENERATE_COMMENTS, generateComments) - } - - def isDisableTests() { - return getHashStructure.getBoolean(PreferenceModel.KEY_DISABLE_TESTS, false) - } - - def setDisableTests(boolean disableTests) { - getHashStructure.putBoolean(PreferenceModel.KEY_DISABLE_TESTS, disableTests) - } - - def getSuitePath() { - return getHashStructure.getString(PreferenceModel.KEY_SUITE_PATH, "alltests") - } - - def setSuitePath(String suitePath) { - getHashStructure.putString(PreferenceModel.KEY_SUITE_PATH, suitePath) - } - - def getIndentSpaces() { - return getHashStructure.getInt(PreferenceModel.KEY_INDENT_SPACES, 3) - } - - def setIndentSpaces(int indentSpaces) { - getHashStructure.putInt(PreferenceModel.KEY_INDENT_SPACES, indentSpaces) - } - - def isGenerateFiles() { - return getHashStructure.getBoolean(PreferenceModel.KEY_GENERATE_FILES, true) - } - - def setGenerateFiles(boolean generateFiles) { - getHashStructure.putBoolean(PreferenceModel.KEY_GENERATE_FILES, generateFiles) - } - - def getOutputDirectory() { - return getHashStructure.getString(PreferenceModel.KEY_OUTPUT_DIRECTORY, DEFAULT_OUTPUT_DIRECTORY) - } - - def setOutputDirectory(String outputDirectory) { - val dir = if (outputDirectory.empty) {DEFAULT_OUTPUT_DIRECTORY} else {outputDirectory} - getHashStructure.putString(PreferenceModel.KEY_OUTPUT_DIRECTORY, dir) - } - - def isDeleteExistingFiles() { - return getHashStructure.getBoolean(PreferenceModel.KEY_DELETE_EXISTING_FILES, false) - } - - def setDeleteExistingFiles(boolean deleteExistingFiles) { - getHashStructure.putBoolean(PreferenceModel.KEY_DELETE_EXISTING_FILES, deleteExistingFiles) - } - - def getRootFolderInOddgenView() { - return getHashStructure.getString(PreferenceModel.KEY_ROOT_FOLDER_IN_ODDGEN_VIEW, "utPLSQL") - } - - def setRootFolderInOddgenView(String rootFolder) { - val folder = if (rootFolder.empty) {"utPLSQL"} else {rootFolder} - getHashStructure.putString(PreferenceModel.KEY_ROOT_FOLDER_IN_ODDGEN_VIEW, folder) - } - - override toString() { - new ToStringBuilder(this).addAllFields.toString - } -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Counter.java b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Counter.java new file mode 100644 index 00000000..77ffceb9 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Counter.java @@ -0,0 +1,78 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.runner; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class Counter { + private Integer disabled; + private Integer success; + private Integer failure; + private Integer error; + private Integer warning; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("disabled", disabled) + .append("success", success) + .append("failure", failure) + .append("error", error) + .append("warning", warning) + .toString(); + } + + public Integer getDisabled() { + return disabled; + } + + public void setDisabled(final Integer disabled) { + this.disabled = disabled; + } + + public Integer getSuccess() { + return success; + } + + public void setSuccess(final Integer success) { + this.success = success; + } + + public Integer getFailure() { + return failure; + } + + public void setFailure(final Integer failure) { + this.failure = failure; + } + + public Integer getError() { + return error; + } + + public void setError(final Integer error) { + this.error = error; + } + + public Integer getWarning() { + return warning; + } + + public void setWarning(final Integer warning) { + this.warning = warning; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Counter.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Counter.xtend deleted file mode 100644 index 0d496e42..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Counter.xtend +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.runner - -import org.eclipse.xtend.lib.annotations.Accessors -import org.utplsql.sqldev.model.AbstractModel - -@Accessors -class Counter extends AbstractModel { - Integer disabled - Integer success - Integer failure - Integer error - Integer warning -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Expectation.java b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Expectation.java new file mode 100644 index 00000000..2738bce1 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Expectation.java @@ -0,0 +1,100 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.runner; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class Expectation { + private String description; + private String message; + private String caller; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("description", description) + .append("message", message) + .append("caller", caller) + .append("failureText", getFailureText()) + .append("shortFailureText", getShortFailureText()) + .append("callerLine", getCallerLine()) + .toString(); + } + + public String getFailureText() { + final StringBuilder sb = new StringBuilder(); + sb.append(message.trim()); + if (caller != null) { + sb.append('\n'); + sb.append(caller.trim()); + } + return sb.toString(); + } + + public String getShortFailureText() { + final StringBuilder sb = new StringBuilder(); + if (description != null) { + sb.append(description); + sb.append(" (line "); + sb.append(getCallerLine()); + sb.append(")"); + } else { + sb.append("Line "); + sb.append(getCallerLine()); + } + return sb.toString(); + } + + public Integer getCallerLine() { + Integer line = null; + if (caller != null) { + final Pattern p = Pattern.compile("(?i)\"[^\\\"]+\",\\s+line\\s*([0-9]+)"); + final Matcher m = p.matcher(caller); + if (m.find()) { + line = Integer.valueOf(m.group(1)); + } + } + return line; + } + + public String getDescription() { + return description; + } + + public void setDescription(final String description) { + this.description = description; + } + + public String getMessage() { + return message; + } + + public void setMessage(final String message) { + this.message = message; + } + + public String getCaller() { + return caller; + } + + public void setCaller(final String caller) { + this.caller = caller; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Expectation.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Expectation.xtend deleted file mode 100644 index 67d7cc19..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Expectation.xtend +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.runner - -import java.util.regex.Pattern -import org.eclipse.xtend.lib.annotations.Accessors -import org.utplsql.sqldev.model.AbstractModel - -@Accessors -class Expectation extends AbstractModel { - String description - String message - String caller - - def getFailureText() { - return ''' - «message.trim» - «caller?.trim» - '''.toString.trim - } - - def getShortFailureText() { - return '''«IF description !== null»«description» (line «callerLine»)«ELSE»Line «callerLine»«ENDIF»'''.toString - } - - def getCallerLine() { - var Integer line = null - if (caller !== null) { - val p = Pattern.compile("(?i)\"[^\\\"]+\",\\s+line\\s*([0-9]+)") - val m = p.matcher(caller) - if (m.find) { - line = Integer.valueOf(m.group(1)) - } - } - return line - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Item.java b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Item.java new file mode 100644 index 00000000..70bcef0c --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Item.java @@ -0,0 +1,108 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.runner; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public abstract class Item { + private String id; + private String startTime; + private String endTime; + private Double executionTime; + private Counter counter; + private String errorStack; + private String serverOutput; + private String warnings; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("id", id) + .append("startTime", startTime) + .append("endTime", endTime) + .append("executionTime", executionTime) + .append("counter", counter) + .append("errorStack", errorStack) + .append("serverOutput", serverOutput) + .append("warnings", warnings) + .toString(); + } + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(final String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(final String endTime) { + this.endTime = endTime; + } + + public Double getExecutionTime() { + return executionTime; + } + + public void setExecutionTime(final Double executionTime) { + this.executionTime = executionTime; + } + + public Counter getCounter() { + return counter; + } + + public void setCounter(final Counter counter) { + this.counter = counter; + } + + public String getErrorStack() { + return errorStack; + } + + public void setErrorStack(final String errorStack) { + this.errorStack = errorStack; + } + + public String getServerOutput() { + return serverOutput; + } + + public void setServerOutput(final String serverOutput) { + this.serverOutput = serverOutput; + } + + public String getWarnings() { + return warnings; + } + + public void setWarnings(final String warnings) { + this.warnings = warnings; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Item.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Item.xtend deleted file mode 100644 index 01db70a6..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Item.xtend +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.runner - -import org.eclipse.xtend.lib.annotations.Accessors -import org.utplsql.sqldev.model.AbstractModel - -@Accessors -abstract class Item extends AbstractModel { - String id - String startTime - String endTime - Double executionTime - Counter counter - String errorStack - String serverOutput - String warnings -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostEvent.java b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostEvent.java new file mode 100644 index 00000000..f24f8f52 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostEvent.java @@ -0,0 +1,102 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.runner; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public abstract class PostEvent extends RealtimeReporterEvent { + private String startTime; + private String endTime; + private Double executionTime; + private Counter counter; + private String errorStack; + private String serverOutput; + private String warnings; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("startTime", startTime) + .append("endTime", endTime) + .append("executionTime", executionTime) + .append("counter", counter) + .append("errorStack", errorStack) + .append("serverOutput", serverOutput) + .append("warnings", warnings) + .toString(); + } + + public PostEvent() { + counter = new Counter(); + } + + public String getStartTime() { + return startTime; + } + + public void setStartTime(final String startTime) { + this.startTime = startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(final String endTime) { + this.endTime = endTime; + } + + public Double getExecutionTime() { + return executionTime; + } + + public void setExecutionTime(final Double executionTime) { + this.executionTime = executionTime; + } + + public Counter getCounter() { + return counter; + } + + public void setCounter(final Counter counter) { + this.counter = counter; + } + + public String getErrorStack() { + return errorStack; + } + + public void setErrorStack(final String errorStack) { + this.errorStack = errorStack; + } + + public String getServerOutput() { + return serverOutput; + } + + public void setServerOutput(final String serverOutput) { + this.serverOutput = serverOutput; + } + + public String getWarnings() { + return warnings; + } + + public void setWarnings(final String warnings) { + this.warnings = warnings; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostEvent.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostEvent.xtend deleted file mode 100644 index 67d9c61c..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostEvent.xtend +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.runner - -import org.eclipse.xtend.lib.annotations.Accessors - -@Accessors -abstract class PostEvent extends RealtimeReporterEvent { - String startTime - String endTime - Double executionTime - Counter counter - String errorStack - String serverOutput - String warnings - - new() { - counter = new Counter - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostRunEvent.java b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostRunEvent.java new file mode 100644 index 00000000..8c8dbe2f --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostRunEvent.java @@ -0,0 +1,36 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.runner; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class PostRunEvent extends PostEvent { + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + // ancestor + .append("startTime", getStartTime()) + .append("endTime", getEndTime()) + .append("executionTime", getExecutionTime()) + .append("counter", getCounter()) + .append("errorStack", getErrorStack()) + .append("serverOutput", getServerOutput()) + .append("warnings", getWarnings()) + .toString(); + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostSuiteEvent.java b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostSuiteEvent.java new file mode 100644 index 00000000..292671c4 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostSuiteEvent.java @@ -0,0 +1,47 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.runner; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class PostSuiteEvent extends PostEvent { + private String id; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + // ancestor + .append("startTime", getStartTime()) + .append("endTime", getEndTime()) + .append("executionTime", getExecutionTime()) + .append("counter", getCounter()) + .append("errorStack", getErrorStack()) + .append("serverOutput", getServerOutput()) + .append("warnings", getWarnings()) + // local + .append("id", id) + .toString(); + } + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostSuiteEvent.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostSuiteEvent.xtend deleted file mode 100644 index 10a674ed..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostSuiteEvent.xtend +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.runner - -import org.eclipse.xtend.lib.annotations.Accessors - -@Accessors -class PostSuiteEvent extends PostEvent { - String id -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostTestEvent.java b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostTestEvent.java new file mode 100644 index 00000000..92a43484 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostTestEvent.java @@ -0,0 +1,84 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.runner; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class PostTestEvent extends PostEvent { + private String id; + private Integer testNumber; + private Integer totalNumberOfTests; + private List failedExpectations; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + // ancestor + .append("startTime", getStartTime()) + .append("endTime", getEndTime()) + .append("executionTime", getExecutionTime()) + .append("counter", getCounter()) + .append("errorStack", getErrorStack()) + .append("serverOutput", getServerOutput()) + .append("warnings", getWarnings()) + // local + .append("id", id) + .append("testNumber", testNumber) + .append("totalNumberOfTests", totalNumberOfTests) + .append("failedExpectations", failedExpectations) + .toString(); + } + + public PostTestEvent() { + failedExpectations = new ArrayList<>(); + } + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } + + public Integer getTestNumber() { + return testNumber; + } + + public void setTestNumber(final Integer testNumber) { + this.testNumber = testNumber; + } + + public Integer getTotalNumberOfTests() { + return totalNumberOfTests; + } + + public void setTotalNumberOfTests(final Integer totalNumberOfTests) { + this.totalNumberOfTests = totalNumberOfTests; + } + + public List getFailedExpectations() { + return failedExpectations; + } + + public void setFailedExpectations(final List failedExpectations) { + this.failedExpectations = failedExpectations; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostTestEvent.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostTestEvent.xtend deleted file mode 100644 index bd28ab6c..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostTestEvent.xtend +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.runner - -import java.util.ArrayList -import java.util.List -import org.eclipse.xtend.lib.annotations.Accessors - -@Accessors -class PostTestEvent extends PostEvent { - String id - Integer testNumber - Integer totalNumberOfTests - List failedExpectations - - new() { - failedExpectations = new ArrayList - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreRunEvent.java b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreRunEvent.java new file mode 100644 index 00000000..dd1f0db8 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreRunEvent.java @@ -0,0 +1,55 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.runner; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class PreRunEvent extends RealtimeReporterEvent { + private List items; + private Integer totalNumberOfTests; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("items", items) + .append("totalNumberOfTests", totalNumberOfTests) + .toString(); + } + + public PreRunEvent() { + items = new ArrayList<>(); + } + + public List getItems() { + return items; + } + + public void setItems(final List items) { + this.items = items; + } + + public Integer getTotalNumberOfTests() { + return totalNumberOfTests; + } + + public void setTotalNumberOfTests(final Integer totalNumberOfTests) { + this.totalNumberOfTests = totalNumberOfTests; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreRunEvent.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreRunEvent.xtend deleted file mode 100644 index 5750cd8e..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreRunEvent.xtend +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.runner - -import java.util.ArrayList -import java.util.List -import org.eclipse.xtend.lib.annotations.Accessors - -@Accessors -class PreRunEvent extends RealtimeReporterEvent { - List items - Integer totalNumberOfTests - - new() { - items = new ArrayList - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/oddgen/GenContext.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreSuiteEvent.java similarity index 54% rename from sqldev/src/main/java/org/utplsql/sqldev/model/oddgen/GenContext.xtend rename to sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreSuiteEvent.java index bb4afc77..7b3ccf5c 100644 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/oddgen/GenContext.xtend +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreSuiteEvent.java @@ -13,24 +13,26 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.utplsql.sqldev.model.oddgen +package org.utplsql.sqldev.model.runner; -import java.sql.Connection -import org.eclipse.xtend.lib.annotations.Accessors -import org.utplsql.sqldev.model.AbstractModel +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; -@Accessors -class GenContext extends AbstractModel { - Connection conn - String objectType - String objectName - String testPackagePrefix - String testPackageSuffix - String testUnitPrefix - String testUnitSuffix - int numberOfTestsPerUnit - boolean generateComments - boolean disableTests - String suitePath - int indentSpaces +public class PreSuiteEvent extends RealtimeReporterEvent { + private String id; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("id", id) + .toString(); + } + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } } diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreSuiteEvent.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreSuiteEvent.xtend deleted file mode 100644 index 0304f071..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreSuiteEvent.xtend +++ /dev/null @@ -1,23 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.runner - -import org.eclipse.xtend.lib.annotations.Accessors - -@Accessors -class PreSuiteEvent extends RealtimeReporterEvent { - String id -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreTestEvent.java b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreTestEvent.java new file mode 100644 index 00000000..1fdbd362 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreTestEvent.java @@ -0,0 +1,58 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.runner; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class PreTestEvent extends RealtimeReporterEvent { + private String id; + private Integer testNumber; + private Integer totalNumberOfTests; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("id", id) + .append("testNumber", testNumber) + .append("totalNumberOfTests", totalNumberOfTests) + .toString(); + } + + public String getId() { + return id; + } + + public void setId(final String id) { + this.id = id; + } + + public Integer getTestNumber() { + return testNumber; + } + + public void setTestNumber(final Integer testNumber) { + this.testNumber = testNumber; + } + + public Integer getTotalNumberOfTests() { + return totalNumberOfTests; + } + + public void setTotalNumberOfTests(final Integer totalNumberOfTests) { + this.totalNumberOfTests = totalNumberOfTests; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreTestEvent.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreTestEvent.xtend deleted file mode 100644 index a54e3c5e..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PreTestEvent.xtend +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.runner - -import org.eclipse.xtend.lib.annotations.Accessors - -@Accessors -class PreTestEvent extends RealtimeReporterEvent { - String id - Integer testNumber - Integer totalNumberOfTests -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostRunEvent.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/RealtimeReporterEvent.java similarity index 81% rename from sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostRunEvent.xtend rename to sqldev/src/main/java/org/utplsql/sqldev/model/runner/RealtimeReporterEvent.java index ee5edc30..3f7d7d1d 100644 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/PostRunEvent.xtend +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/RealtimeReporterEvent.java @@ -13,10 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.utplsql.sqldev.model.runner +package org.utplsql.sqldev.model.runner; -import org.eclipse.xtend.lib.annotations.Accessors - -@Accessors -class PostRunEvent extends PostEvent { +public abstract class RealtimeReporterEvent { } diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Run.java b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Run.java new file mode 100644 index 00000000..ed775d36 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Run.java @@ -0,0 +1,236 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.runner; + +import java.util.LinkedHashMap; +import java.util.List; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class Run { + private String reporterId; + private String connectionName; + private List pathList; + private Integer currentTestNumber; + private Test currentTest; + private Integer totalNumberOfTests; + private String startTime; + private String endTime; + private Double executionTime; + private Counter counter; + private Integer infoCount; + private String errorStack; + private String serverOutput; + private LinkedHashMap tests; + private String status; + private Long start; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("reporterId", reporterId) + .append("connectionName", connectionName) + .append("pathList", pathList) + .append("currentTestNumber", currentTestNumber) + .append("currentTest", currentTest) + .append("totalNumberOfTests", totalNumberOfTests) + .append("startTime", startTime) + .append("endTime", endTime) + .append("executionTime", executionTime) + .append("counter", counter) + .append("infoCount", infoCount) + .append("errorStack", errorStack) + .append("serverOutput", serverOutput) + .append("tests", tests) + .append("status", status) + .append("start", start) + .append("endTime", endTime) + .append("totalNumberOfCompletedTests", getTotalNumberOfCompletedTests()) + .toString(); + } + + public Run(final String reporterId, final String connectionName, final List pathList) { + this.reporterId = reporterId; + this.connectionName = connectionName; + this.pathList = pathList; + counter = new Counter(); + tests = new LinkedHashMap<>(); + } + + public void setStartTime(final String startTime) { + this.startTime = startTime; + start = System.currentTimeMillis(); + } + + public String getName() { + final String time = startTime.substring(11, 19); + final String conn = connectionName != null ? connectionName.substring(15) : "n/a"; + final StringBuilder sb = new StringBuilder(); + sb.append(time); + sb.append(" ("); + sb.append(conn); + sb.append(")"); + return sb.toString(); + } + + public void put(final List items) { + for (final Item item : items) { + if (item instanceof Test) { + tests.put(((Test) item).getId(), (Test) item); + } + if (item instanceof Suite) { + put(((Suite) item).getItems()); + } + } + } + + public Test getTest(final String id) { + return tests.get(id); + } + + public int getTotalNumberOfCompletedTests() { + if (counter.getDisabled() == null || counter.getSuccess() == null || counter.getFailure() == null + || counter.getError() == null) { + return -1; + } + return counter.getDisabled() + counter.getSuccess() + counter.getFailure() + counter.getError(); + } + + public String getReporterId() { + return reporterId; + } + + public void setReporterId(final String reporterId) { + this.reporterId = reporterId; + } + + public String getConnectionName() { + return connectionName; + } + + public void setConnectionName(final String connectionName) { + this.connectionName = connectionName; + } + + public List getPathList() { + return pathList; + } + + public void setPathList(final List pathList) { + this.pathList = pathList; + } + + public Integer getCurrentTestNumber() { + return currentTestNumber; + } + + public void setCurrentTestNumber(final Integer currentTestNumber) { + this.currentTestNumber = currentTestNumber; + } + + public Test getCurrentTest() { + return currentTest; + } + + public void setCurrentTest(final Test currentTest) { + this.currentTest = currentTest; + } + + public Integer getTotalNumberOfTests() { + return totalNumberOfTests; + } + + public void setTotalNumberOfTests(final Integer totalNumberOfTests) { + this.totalNumberOfTests = totalNumberOfTests; + } + + public String getStartTime() { + return startTime; + } + + public String getEndTime() { + return endTime; + } + + public void setEndTime(final String endTime) { + this.endTime = endTime; + } + + public Double getExecutionTime() { + return executionTime; + } + + public void setExecutionTime(final Double executionTime) { + this.executionTime = executionTime; + } + + public Counter getCounter() { + return counter; + } + + public void setCounter(final Counter counter) { + this.counter = counter; + } + + public Integer getInfoCount() { + return infoCount; + } + + public void setInfoCount(final Integer infoCount) { + this.infoCount = infoCount; + } + + public String getErrorStack() { + return errorStack; + } + + public void setErrorStack(final String errorStack) { + this.errorStack = errorStack; + } + + public String getServerOutput() { + return serverOutput; + } + + public void setServerOutput(final String serverOutput) { + this.serverOutput = serverOutput; + } + + public LinkedHashMap getTests() { + return tests; + } + + public void setTests(final LinkedHashMap tests) { + this.tests = tests; + } + + public String getStatus() { + return status; + } + + public void setStatus(final String status) { + this.status = status; + } + + public Long getStart() { + return start; + } + + public void setStart(final Long start) { + this.start = start; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Run.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Run.xtend deleted file mode 100644 index 37b11926..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Run.xtend +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.runner - -import java.util.LinkedHashMap -import java.util.List -import org.eclipse.xtend.lib.annotations.Accessors -import org.utplsql.sqldev.model.AbstractModel - -@Accessors -class Run extends AbstractModel { - String reporterId - String connectionName - List pathList - Integer currentTestNumber - Test currentTest - Integer totalNumberOfTests - String startTime - String endTime - Double executionTime - Counter counter - Integer infoCount - String errorStack - String serverOutput - LinkedHashMap tests - String status - Long start - - new(String reporterId, String connectionName, List pathList) { - this.reporterId = reporterId - this.connectionName = connectionName - this.pathList = pathList - this.counter = new Counter - this.tests = new LinkedHashMap - } - - def void setStartTime(String startTime) { - this.startTime = startTime - start = System.currentTimeMillis - } - - def getName() { - val time = startTime.substring(11,19) - val conn = connectionName?.substring(15) - return '''«time» («conn»)''' - } - - def void put(List items) { - for (item : items) { - if (item instanceof Test) { - this.tests.put(item.id, item) - } - if (item instanceof Suite) { - item.items.put - } - } - } - - def getTest(String id) { - return tests.get(id) - } - - def getTotalNumberOfCompletedTests() { - return counter.disabled + counter.success + counter.failure + counter.error - } - -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Suite.java b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Suite.java new file mode 100644 index 00000000..b6ec4be2 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Suite.java @@ -0,0 +1,75 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.runner; + +import java.util.ArrayList; +import java.util.List; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class Suite extends Item { + private String name; + private String description; + private List items; + + public Suite() { + items = new ArrayList<>(); + } + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + // ancestor + .append("id", getId()) + .append("startTime", getStartTime()) + .append("endTime", getEndTime()) + .append("executionTime", getExecutionTime()) + .append("counter", getCounter()) + .append("errorStack", getErrorStack()) + .append("serverOutput", getServerOutput()) + .append("warnings", getWarnings()) + // local + .append("name", name) + .append("description", description) + .append("items", items) + .toString(); + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(final String description) { + this.description = description; + } + + public List getItems() { + return items; + } + + public void setItems(final List items) { + this.items = items; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Suite.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Suite.xtend deleted file mode 100644 index d75efadc..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Suite.xtend +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.runner - -import java.util.ArrayList -import java.util.List -import org.eclipse.xtend.lib.annotations.Accessors - -@Accessors -class Suite extends Item { - String name - String description - List items - - new() { - items = new ArrayList - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Test.java b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Test.java new file mode 100644 index 00000000..9e7d78ad --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Test.java @@ -0,0 +1,172 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.runner; + +import java.util.List; + +import javax.swing.Icon; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; +import org.utplsql.sqldev.resources.UtplsqlResources; + +public class Test extends Item { + private String executableType; + private String ownerName; + private String objectName; + private String procedureName; + private Boolean disabled; + private String name; + private String description; + private Integer testNumber; + private List failedExpectations; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + // ancestor + .append("id", getId()) + .append("startTime", getStartTime()) + .append("endTime", getEndTime()) + .append("executionTime", getExecutionTime()) + .append("counter", getCounter()) + .append("errorStack", getErrorStack()) + .append("serverOutput", getServerOutput()) + .append("warnings", getWarnings()) + // local + .append("executableType", executableType) + .append("ownerName", ownerName) + .append("objectName", objectName) + .append("procedureName", procedureName) + .append("disabled", disabled) + .append("name", name) + .append("description", description) + .append("testNumber", testNumber) + .append("failedExpectations", failedExpectations) + .append("statusIcon", getStatusIcon()) + .append("warningIcon", getWarningIcon()) + .append("infoIcon", getInfoIcon()) + .toString(); + } + + public Icon getStatusIcon() { + Icon icon = null; + if (getStartTime() != null && getEndTime() == null) { + icon = UtplsqlResources.getIcon("PROGRESS_ICON"); + } else { + if (getCounter() != null) { + if (getCounter().getSuccess() > 0) { + icon = UtplsqlResources.getIcon("SUCCESS_ICON"); + } else if (getCounter().getError() > 0) { + icon = UtplsqlResources.getIcon("ERROR_ICON"); + } else if (getCounter().getFailure() > 0) { + icon = UtplsqlResources.getIcon("FAILURE_ICON"); + } else if (getCounter().getDisabled() > 0) { + icon = UtplsqlResources.getIcon("DISABLED_ICON"); + } + } + } + return icon; + } + + public Icon getWarningIcon() { + Icon icon = null; + if (getCounter() != null && getCounter().getWarning() > 0) { + icon = UtplsqlResources.getIcon("WARNING_ICON"); + } + return icon; + } + + public Icon getInfoIcon() { + Icon icon = null; + if (getServerOutput() != null && getServerOutput().length() > 0) { + icon = UtplsqlResources.getIcon("INFO_ICON"); + } + return icon; + } + + public String getExecutableType() { + return executableType; + } + + public void setExecutableType(final String executableType) { + this.executableType = executableType; + } + + public String getOwnerName() { + return ownerName; + } + + public void setOwnerName(final String ownerName) { + this.ownerName = ownerName; + } + + public String getObjectName() { + return objectName; + } + + public void setObjectName(final String objectName) { + this.objectName = objectName; + } + + public String getProcedureName() { + return procedureName; + } + + public void setProcedureName(final String procedureName) { + this.procedureName = procedureName; + } + + public Boolean getDisabled() { + return disabled; + } + + public void setDisabled(final Boolean disabled) { + this.disabled = disabled; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getDescription() { + return description; + } + + public void setDescription(final String description) { + this.description = description; + } + + public Integer getTestNumber() { + return testNumber; + } + + public void setTestNumber(final Integer testNumber) { + this.testNumber = testNumber; + } + + public List getFailedExpectations() { + return failedExpectations; + } + + public void setFailedExpectations(final List failedExpectations) { + this.failedExpectations = failedExpectations; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Test.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Test.xtend deleted file mode 100644 index 30070e85..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/runner/Test.xtend +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.runner - -import java.util.List -import javax.swing.Icon -import org.eclipse.xtend.lib.annotations.Accessors -import org.utplsql.sqldev.resources.UtplsqlResources - -@Accessors -class Test extends Item { - String executableType - String ownerName - String objectName - String procedureName - Boolean disabled - String name - String description - Integer testNumber - List failedExpectations - - def getStatusIcon() { - var Icon icon = null - if (startTime !== null && endTime === null ) { - icon = UtplsqlResources.getIcon("PROGRESS_ICON") - } else { - if (counter !== null) { - if (counter.success > 0) { - icon = UtplsqlResources.getIcon("SUCCESS_ICON") - } else if (counter.error > 0) { - icon = UtplsqlResources.getIcon("ERROR_ICON") - } else if (counter.failure > 0) { - icon = UtplsqlResources.getIcon("FAILURE_ICON") - } else if (counter.disabled > 0) { - icon = UtplsqlResources.getIcon("DISABLED_ICON") - } - } - } - return icon - } - - def getWarningIcon() { - var Icon icon = null - if (counter !== null) { - if (counter.warning > 0) { - icon = UtplsqlResources.getIcon("WARNING_ICON") - } - } - return icon - } - - def getInfoIcon() { - var Icon icon = null - if (serverOutput !== null && serverOutput.length > 0) { - icon = UtplsqlResources.getIcon("INFO_ICON") - } - return icon - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/ut/Annotation.java b/sqldev/src/main/java/org/utplsql/sqldev/model/ut/Annotation.java new file mode 100644 index 00000000..3f69ef77 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/ut/Annotation.java @@ -0,0 +1,78 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.ut; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class Annotation { + private String objectOwner; + private String objectName; + private String name; + private String text; + private String subobjectName; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("objectOwner", objectOwner) + .append("objectName", objectName) + .append("name", name) + .append("text", text) + .append("subobjectName", subobjectName) + .toString(); + } + + public String getObjectOwner() { + return objectOwner; + } + + public void setObjectOwner(final String objectOwner) { + this.objectOwner = objectOwner; + } + + public String getObjectName() { + return objectName; + } + + public void setObjectName(final String objectName) { + this.objectName = objectName; + } + + public String getName() { + return name; + } + + public void setName(final String name) { + this.name = name; + } + + public String getText() { + return text; + } + + public void setText(final String text) { + this.text = text; + } + + public String getSubobjectName() { + return subobjectName; + } + + public void setSubobjectName(final String subobjectName) { + this.subobjectName = subobjectName; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/ut/Annotation.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/ut/Annotation.xtend deleted file mode 100644 index ee658e91..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/ut/Annotation.xtend +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.ut - -import org.eclipse.xtend.lib.annotations.Accessors -import org.utplsql.sqldev.model.AbstractModel - -@Accessors -class Annotation extends AbstractModel { - String objectOwner - String objectName - String name - String text - String subobjectName -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/ut/OutputLines.java b/sqldev/src/main/java/org/utplsql/sqldev/model/ut/OutputLines.java new file mode 100644 index 00000000..a8a041ae --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/model/ut/OutputLines.java @@ -0,0 +1,48 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.model.ut; + +import org.springframework.core.style.ToStringCreator; +import org.utplsql.sqldev.model.JsonToStringStyler; + +public class OutputLines { + private String[] lines; + private Integer numlines; + + @Override + public String toString() { + return new ToStringCreator(this, JsonToStringStyler.INSTANCE) + .append("lines", lines) + .append("numlines", numlines) + .toString(); + } + + public String[] getLines() { + return lines; + } + + public void setLines(final String[] lines) { + this.lines = lines; + } + + public Integer getNumlines() { + return numlines; + } + + public void setNumlines(final Integer numlines) { + this.numlines = numlines; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/model/ut/OutputLines.xtend b/sqldev/src/main/java/org/utplsql/sqldev/model/ut/OutputLines.xtend deleted file mode 100644 index 7b1c946c..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/model/ut/OutputLines.xtend +++ /dev/null @@ -1,25 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.model.ut - -import org.eclipse.xtend.lib.annotations.Accessors -import org.utplsql.sqldev.model.AbstractModel - -@Accessors -class OutputLines extends AbstractModel { - String[] lines; - Integer numlines; -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/oddgen/RunGenerator.java b/sqldev/src/main/java/org/utplsql/sqldev/oddgen/RunGenerator.java new file mode 100644 index 00000000..215baf2f --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/oddgen/RunGenerator.java @@ -0,0 +1,180 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.oddgen; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.stream.Collectors; + +import org.oddgen.sqldev.generators.OddgenGenerator2; +import org.oddgen.sqldev.generators.model.Node; +import org.utplsql.sqldev.dal.UtplsqlDao; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.StringTools; +import org.utplsql.sqldev.model.preference.PreferenceModel; +import org.utplsql.sqldev.resources.UtplsqlResources; + +import oracle.ide.config.Preferences; + +public class RunGenerator implements OddgenGenerator2 { + public static final String YES = "Yes"; + public static final String NO = "No"; + public static final String RESET_PACKAGE = UtplsqlResources.getString("PREF_RESET_PACKAGE_LABEL"); + public static final String CLEAR_SCREEN = UtplsqlResources.getString("PREF_CLEAR_SCREEN_LABEL"); + public static final String INDENT_SPACES = UtplsqlResources.getString("PREF_INDENT_SPACES_LABEL"); + + // oddgen node cache + private List runnables = null; + + @Override + public boolean isSupported(final Connection conn) { + return DatabaseTools.isSupported(conn); + } + + @Override + public String getName(final Connection conn) { + return "Run test"; + } + + @Override + public String getDescription(final Connection conn) { + return "Runs utPLSQL test packages in the current user."; + } + + @Override + public List getFolders(final Connection conn) { + final PreferenceModel preferences = PreferenceModel.getInstance(Preferences.getPreferences()); + final ArrayList folders = new ArrayList<>(); + for (String f : preferences.getRootFolderInOddgenView().split(",")) { + if (f != null) { + folders.add(f.trim()); + } + } + return folders; + } + + @Override + public String getHelp(final Connection conn) { + return "

not yet available

"; + } + + @Override + public List getNodes(final Connection conn, final String parentNodeId) { + // oddgen asks for children for each parent node, regardless of load strategy (eager/lazy) + // oddgen does not know about the load strategy, hence caching is the responsibility of the generator + if (runnables == null) { + final PreferenceModel preferences = PreferenceModel.getInstance(Preferences.getPreferences()); + final LinkedHashMap params = new LinkedHashMap<>(); + params.put(RESET_PACKAGE, preferences.isResetPackage() ? YES : NO); + params.put(CLEAR_SCREEN, preferences.isClearScreen() ? YES : NO); + params.put(INDENT_SPACES, String.valueOf(preferences.getIndentSpaces())); + final UtplsqlDao dao = new UtplsqlDao(conn); + // load node tree eagerly (all nodes in one go) + runnables = dao.runnables(); + for (final Node node : runnables) { + node.setParams(params); + } + } + return runnables; + } + + @Override + public HashMap> getLov(final Connection conn, final LinkedHashMap params, final List nodes) { + final HashMap> lov = new HashMap<>(); + lov.put(RESET_PACKAGE, Arrays.asList(YES, NO)); + lov.put(CLEAR_SCREEN, Arrays.asList(YES, NO)); + lov.put(INDENT_SPACES, Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8")); + return lov; + } + + @Override + public HashMap getParamStates(final Connection conn, final LinkedHashMap params, final List nodes) { + return new HashMap<>(); + } + + private String getPath(final Node node, final Connection conn) { + if ("SUITE".equals(node.getId()) || "SUITEPATH".equals(node.getId())) { + return DatabaseTools.getUser(conn); + } else { + return node.getId(); + } + } + + public ArrayList dedup(final List nodes) { + final HashSet set = new HashSet<>(); + for (final Node node : nodes) { + set.add(node.getId()); + } + final ArrayList ret = new ArrayList<>(); + for (final Node node : nodes) { + if (!set.contains(node.getParentId())) { + ret.add(node); + } + } + return ret; + } + + @Override + public String generateProlog(final Connection conn, final List nodes) { + final ArrayList dedupNodes = dedup(nodes); + final LinkedHashMap params = dedupNodes.get(0).getParams(); + final StringBuilder sb = new StringBuilder(); + if (YES.equals(params.get(RESET_PACKAGE))) { + sb.append("EXECUTE dbms_session.reset_package;\n"); + } + sb.append("SET SERVEROUTPUT ON SIZE UNLIMITED\n"); + if (YES.equals(params.get(CLEAR_SCREEN))) { + sb.append("CLEAR SCREEN\n"); + } + if (dedupNodes.size() == 1) { + sb.append("EXECUTE ut.run('"); + sb.append(getPath(dedupNodes.get(0), conn)); + sb.append("');\n"); + } else { + final List paths = dedupNodes.stream().map(node -> getPath(node, conn)).collect(Collectors.toList()); + sb.append("BEGIN\n"); + sb.append("\tut.run(\n"); + sb.append("\t\tut_varchar2_list(\n"); + sb.append(StringTools.getCSV(paths, "\t\t\t")); + sb.append("\t\t)\n"); + sb.append("\t);\n"); + sb.append("END;\n"); + sb.append("/\n"); + } + final String ret = sb.toString(); + return StringTools.replaceTabsWithSpaces(ret, Integer.valueOf(params.get(INDENT_SPACES))); + } + + @Override + public String generateSeparator(final Connection conn) { + return ""; + } + + @Override + public String generateEpilog(final Connection conn, final List nodes) { + return ""; + } + + @Override + public String generate(final Connection conn, final Node node) { + return ""; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/oddgen/RunGenerator.xtend b/sqldev/src/main/java/org/utplsql/sqldev/oddgen/RunGenerator.xtend deleted file mode 100644 index b321b77f..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/oddgen/RunGenerator.xtend +++ /dev/null @@ -1,179 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.oddgen - -import java.sql.Connection -import java.util.ArrayList -import java.util.HashMap -import java.util.HashSet -import java.util.LinkedHashMap -import java.util.List -import oracle.ide.config.Preferences -import org.oddgen.sqldev.generators.OddgenGenerator2 -import org.oddgen.sqldev.generators.model.Node -import org.utplsql.sqldev.dal.UtplsqlDao -import org.utplsql.sqldev.model.preference.PreferenceModel -import org.utplsql.sqldev.resources.UtplsqlResources - -class RunGenerator implements OddgenGenerator2 { - - public static val YES = "Yes" - public static val NO = "No" - - public static var RESET_PACKAGE = UtplsqlResources.getString("PREF_RESET_PACKAGE_LABEL") - public static var CLEAR_SCREEN = UtplsqlResources.getString("PREF_CLEAR_SCREEN_LABEL") - public static var INDENT_SPACES = UtplsqlResources.getString("PREF_INDENT_SPACES_LABEL") - - // oddgen node cache - var List runnables = null; - - override isSupported(Connection conn) { - var ret = false - if (conn !== null) { - if (conn.metaData.databaseProductName.startsWith("Oracle")) { - if (conn.metaData.databaseMajorVersion == 11) { - if (conn.metaData.databaseMinorVersion >= 2) { - ret = true - } - } else if (conn.metaData.databaseMajorVersion > 11) { - ret = true - } - } - } - return ret - } - - override getName(Connection conn) { - return "Run test" - } - - override getDescription(Connection conn) { - return "Runs utPLSQL test packages in the current user." - } - - override getFolders(Connection conn) { - val preferences = PreferenceModel.getInstance(Preferences.preferences) - val folders = new ArrayList - for (f : preferences.rootFolderInOddgenView.split(",").filter[!it.empty]) { - folders.add(f.trim) - } - return folders - } - - override getHelp(Connection conn) { - return "

not yet available

" - } - - override getNodes(Connection conn, String parentNodeId) { - // oddgen asks for children for each parent node, regardless of load strategy (eager/lazy) - // oddgen does not know about the load strategy, hence caching is the responsibility of the generator - if (runnables === null) { - val preferences = PreferenceModel.getInstance(Preferences.preferences) - val params = new LinkedHashMap() - params.put(RESET_PACKAGE, if (preferences.resetPackage) {YES} else {NO}) - params.put(CLEAR_SCREEN, if (preferences.clearScreen) {YES} else {NO}) - params.put(INDENT_SPACES, String.valueOf(preferences.indentSpaces)) - val UtplsqlDao dao = new UtplsqlDao(conn) - // load node tree eagerly (all nodes in one go) - runnables = dao.runnables - for (node : runnables) { - node.params = params - } - } - return runnables - } - - override getLov(Connection conn, LinkedHashMap params, List nodes) { - val lov = new HashMap>() - lov.put(RESET_PACKAGE, #[YES, NO]) - lov.put(CLEAR_SCREEN, #[YES, NO]) - lov.put(INDENT_SPACES, #["1", "2", "3", "4", "5", "6", "7", "8"]) - return lov - } - - override getParamStates(Connection conn, LinkedHashMap params, List nodes) { - return new HashMap - } - - private def getPath(Node node, Connection conn) { - if (node.id == "SUITE" || node.id == "SUITEPATH") { - return conn.metaData.userName - } else { - return node.id - } - } - - private def replaceTabsWithSpaces(CharSequence input, int indentSpaces) { - val spaces = String.format("%1$"+indentSpaces+"s", "") - return input.toString.replace("\t", spaces) - } - - def dedup(List nodes) { - val set = new HashSet - for (node : nodes) { - set.add(node.id) - } - val ret = new ArrayList - for (node : nodes) { - if (!set.contains(node.parentId)) { - ret.add(node) - } - } - return ret - } - - override generateProlog(Connection conn, List nodes) { - val dedupNodes = nodes.dedup - val params = dedupNodes.get(0).params - val ret = ''' - «IF params.get(RESET_PACKAGE) == YES» - EXECUTE dbms_session.reset_package; - «ENDIF» - SET SERVEROUTPUT ON SIZE UNLIMITED - «IF params.get(CLEAR_SCREEN) == YES» - CLEAR SCREEN - «ENDIF» - «IF dedupNodes.size == 1» - EXECUTE ut.run('«dedupNodes.get(0).getPath(conn)»'); - «ELSE» - BEGIN - ut.run( - ut_varchar2_list( - «FOR node : dedupNodes SEPARATOR ","» - '«node.getPath(conn)»' - «ENDFOR» - ) - ); - END; - / - «ENDIF» - ''' - return ret.replaceTabsWithSpaces(Integer.valueOf(params.get(INDENT_SPACES))) - } - - override generateSeparator(Connection conn) { - return "" - } - - override generateEpilog(Connection conn, List nodes) { - return "" - } - - override generate(Connection conn, Node node) { - return "" - } - -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/oddgen/TestGenerator.java b/sqldev/src/main/java/org/utplsql/sqldev/oddgen/TestGenerator.java new file mode 100644 index 00000000..77b2aa42 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/oddgen/TestGenerator.java @@ -0,0 +1,303 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.oddgen; + +import java.io.File; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.logging.Logger; + +import org.oddgen.sqldev.generators.OddgenGenerator2; +import org.oddgen.sqldev.generators.model.Node; +import org.oddgen.sqldev.generators.model.NodeTools; +import org.oddgen.sqldev.plugin.templates.TemplateTools; +import org.utplsql.sqldev.dal.UtplsqlDao; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.oddgen.GenContext; +import org.utplsql.sqldev.model.preference.PreferenceModel; +import org.utplsql.sqldev.resources.UtplsqlResources; + +import oracle.ide.config.Preferences; + +public class TestGenerator implements OddgenGenerator2 { + private static final Logger logger = Logger.getLogger(TestGenerator.class.getName()); + + public static final String YES = "Yes"; + public static final String NO = "No"; + + public static final String GENERATE_FILES = UtplsqlResources.getString("PREF_GENERATE_FILES_LABEL"); + public static final String OUTPUT_DIRECTORY = UtplsqlResources.getString("PREF_OUTPUT_DIRECTORY_LABEL"); + public static final String DELETE_EXISTING_FILES = UtplsqlResources.getString("PREF_DELETE_EXISTING_FILES_LABEL"); + public static final String TEST_PACKAGE_PREFIX = UtplsqlResources.getString("PREF_TEST_PACKAGE_PREFIX_LABEL"); + public static final String TEST_PACKAGE_SUFFIX = UtplsqlResources.getString("PREF_TEST_PACKAGE_SUFFIX_LABEL"); + public static final String TEST_UNIT_PREFIX = UtplsqlResources.getString("PREF_TEST_UNIT_PREFIX_LABEL"); + public static final String TEST_UNIT_SUFFIX = UtplsqlResources.getString("PREF_TEST_UNIT_SUFFIX_LABEL"); + public static final String NUMBER_OF_TESTS_PER_UNIT = UtplsqlResources.getString("PREF_NUMBER_OF_TESTS_PER_UNIT_LABEL"); + public static final String GENERATE_COMMENTS = UtplsqlResources.getString("PREF_GENERATE_COMMENTS_LABEL"); + public static final String DISABLE_TESTS = UtplsqlResources.getString("PREF_DISABLE_TESTS_LABEL"); + public static final String SUITE_PATH = UtplsqlResources.getString("PREF_SUITE_PATH_LABEL"); + public static final String INDENT_SPACES = UtplsqlResources.getString("PREF_INDENT_SPACES_LABEL"); + + private final NodeTools nodeTools = new NodeTools(); + private final TemplateTools templateTools = new TemplateTools(); + private final ArrayList consoleOutput = new ArrayList<>(); + + private GenContext toContext(final Node node) { + final GenContext context = new GenContext(); + context.setObjectType(nodeTools.toObjectType(node)); + context.setObjectName(nodeTools.toObjectName(node)); + context.setTestPackagePrefix(node.getParams().get(TEST_PACKAGE_PREFIX).toLowerCase()); + context.setTestPackageSuffix(node.getParams().get(TEST_PACKAGE_SUFFIX).toLowerCase()); + context.setTestUnitPrefix(node.getParams().get(TEST_UNIT_PREFIX).toLowerCase()); + context.setTestUnitSuffix(node.getParams().get(TEST_UNIT_SUFFIX).toLowerCase()); + context.setNumberOfTestsPerUnit((Integer.valueOf(node.getParams().get(NUMBER_OF_TESTS_PER_UNIT))).intValue()); + context.setGenerateComments(YES.equals(node.getParams().get(GENERATE_COMMENTS))); + context.setDisableTests(YES.equals(node.getParams().get(DISABLE_TESTS))); + context.setSuitePath(node.getParams().get(SUITE_PATH).toLowerCase()); + context.setIndentSpaces((Integer.valueOf(node.getParams().get(INDENT_SPACES))).intValue()); + return context; + } + + private void resetConsoleOutput() { + consoleOutput.clear(); + } + + private void saveConsoleOutput(final String s) { + if (s != null) { + for (final String line : s.split("[\\n\\r]+")) { + consoleOutput.add(line); + } + } + } + + private void logConsoleOutput() { + for (final String line : consoleOutput) { + if (line.contains("error") || line.startsWith("Cannot")) { + logger.severe(line); + } else { + logger.fine(line); + } + } + } + + private String deleteFile(final File file) { + String ret = null; + if (file.delete()) { + StringBuilder sb = new StringBuilder(); + sb.append(file.getAbsoluteFile()); + sb.append(" deleted."); + ret = sb.toString(); + } else { + StringBuilder sb = new StringBuilder(); + sb.append("Cannot delete file "); + sb.append(file.getAbsoluteFile()); + sb.append("."); + ret = sb.toString(); + } + return ret; + } + + private CharSequence deleteFiles(final String directory) { + StringBuilder sb = new StringBuilder(); + final File dir = new File(directory); + for (final File file : dir.listFiles()) { + if (!file.isDirectory() && (file.getName().endsWith(".pks") || file.getName().endsWith(".pkb"))) { + sb.append(deleteFile(file)); + sb.append('\n'); + } + } + return sb; + } + + @Override + public boolean isSupported(final Connection conn) { + return DatabaseTools.isSupported(conn); + } + + @Override + public String getName(final Connection conn) { + return "Generate test"; + } + + @Override + public String getDescription(final Connection conn) { + return "Generates utPLSQL test packages for public units in packages, types, functions and procedures found in the current schema."; + } + + @Override + public List getFolders(final Connection conn) { + final PreferenceModel preferences = PreferenceModel.getInstance(Preferences.getPreferences()); + final ArrayList folders = new ArrayList<>(); + for (String f : preferences.getRootFolderInOddgenView().split(",")) { + if (f != null) { + folders.add(f.trim()); + } + } + return folders; + } + + @Override + public String getHelp(final Connection conn) { + return "

not yet available

"; + } + + @Override + public List getNodes(final Connection conn, final String parentNodeId) { + final PreferenceModel preferences = PreferenceModel.getInstance(Preferences.getPreferences()); + final LinkedHashMap params = new LinkedHashMap<>(); + params.put(GENERATE_FILES, preferences.isGenerateFiles() ? YES : NO); + params.put(OUTPUT_DIRECTORY, preferences.getOutputDirectory()); + params.put(DELETE_EXISTING_FILES, preferences.isDeleteExistingFiles() ? YES : NO); + params.put(TEST_PACKAGE_PREFIX, preferences.getTestPackagePrefix()); + params.put(TEST_PACKAGE_SUFFIX, preferences.getTestPackageSuffix()); + params.put(TEST_UNIT_PREFIX, preferences.getTestUnitPrefix()); + params.put(TEST_UNIT_SUFFIX, preferences.getTestUnitSuffix()); + params.put(NUMBER_OF_TESTS_PER_UNIT, String.valueOf(preferences.getNumberOfTestsPerUnit())); + params.put(GENERATE_COMMENTS, preferences.isGenerateComments() ? YES : NO); + params.put(DISABLE_TESTS, preferences.isDisableTests() ? YES : NO); + params.put(SUITE_PATH, preferences.getSuitePath()); + params.put(INDENT_SPACES, String.valueOf(preferences.getIndentSpaces())); + if (parentNodeId == null || parentNodeId.isEmpty()) { + final Node packageNode = new Node(); + packageNode.setId("PACKAGE"); + packageNode.setParams(params); + packageNode.setLeaf(false); + packageNode.setGeneratable(true); + packageNode.setMultiselectable(true); + final Node typeNode = new Node(); + typeNode.setId("TYPE"); + typeNode.setParams(params); + typeNode.setLeaf(false); + typeNode.setGeneratable(true); + typeNode.setMultiselectable(true); + final Node functionNode = new Node(); + functionNode.setId("FUNCTION"); + functionNode.setParams(params); + functionNode.setLeaf(false); + functionNode.setGeneratable(true); + functionNode.setMultiselectable(true); + final Node procedureNode = new Node(); + procedureNode.setId("PROCEDURE"); + procedureNode.setParams(params); + procedureNode.setLeaf(false); + procedureNode.setGeneratable(true); + procedureNode.setMultiselectable(true); + return Arrays.asList(packageNode, typeNode, functionNode, procedureNode); + } else { + final UtplsqlDao dao = new UtplsqlDao(conn); + final List nodes = dao.testables(parentNodeId); + for (final Node node : nodes) { + node.setParams(params); + } + return nodes; + } + } + + @Override + public HashMap> getLov(final Connection conn, final LinkedHashMap params, + final List nodes) { + final HashMap> lov = new HashMap<>(); + lov.put(NUMBER_OF_TESTS_PER_UNIT, Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8", "9", "10")); + lov.put(INDENT_SPACES, Arrays.asList("1", "2", "3", "4", "5", "6", "7", "8")); + lov.put(GENERATE_COMMENTS, Arrays.asList(YES, NO)); + lov.put(DISABLE_TESTS, Arrays.asList(YES, NO)); + lov.put(GENERATE_FILES, Arrays.asList(YES, NO)); + lov.put(DELETE_EXISTING_FILES, Arrays.asList(YES, NO)); + return lov; + } + + @Override + public HashMap getParamStates(final Connection conn, final LinkedHashMap params, + final List nodes) { + final HashMap paramStates = new HashMap<>(); + paramStates.put(OUTPUT_DIRECTORY, YES.equals(params.get(GENERATE_FILES))); + paramStates.put(DELETE_EXISTING_FILES, YES.equals(params.get(GENERATE_FILES))); + return paramStates; + } + + @Override + public String generateProlog(final Connection conn, final List nodes) { + StringBuilder sb = new StringBuilder(); + final boolean generateFiles = YES.equals(nodes.get(0).getParams().get(GENERATE_FILES)); + final String outputDirectory = nodes.get(0).getParams().get(OUTPUT_DIRECTORY); + final boolean deleteExistingfiles = YES.equals(nodes.get(0).getParams().get(DELETE_EXISTING_FILES)); + if (generateFiles) { + resetConsoleOutput(); + saveConsoleOutput(templateTools.mkdirs(outputDirectory)); + if (deleteExistingfiles) { + saveConsoleOutput(deleteFiles(outputDirectory).toString()); + } + sb.append("--\n"); + sb.append("-- install generated utPLSQL test packages\n"); + sb.append("--\n"); + } + for (final Node node : nodes) { + final GenContext context = toContext(node); + context.setConn(conn); + final TestTemplate testTemplate = new TestTemplate(context); + if (generateFiles) { + final String packageName = context.getTestPackagePrefix() + nodeTools.toObjectName(node) + + context.getTestPackageSuffix(); + final String packagePath = outputDirectory + File.separator + packageName; + saveConsoleOutput(templateTools.writeToFile(packagePath + ".pks", testTemplate.generateSpec())); + saveConsoleOutput(templateTools.writeToFile(packagePath + ".pkb", testTemplate.generateBody())); + sb.append('@'); + sb.append(packagePath); + sb.append(".pks\n"); + sb.append('@'); + sb.append(packagePath); + sb.append(".pkb\n"); + } else { + sb.append(testTemplate.generate()); + sb.append('\n'); + } + } + logConsoleOutput(); + if (generateFiles && consoleOutput.stream().anyMatch(it -> it.contains("error"))) { + sb.append('\n'); + sb.append("--\n"); + sb.append("-- console output produced during the generation of this script (errors found)\n"); + sb.append("--\n"); + sb.append("/*\n\n"); + for (final String line : consoleOutput) { + sb.append(line); + sb.append('\n'); + } + sb.append('\n'); + sb.append("*/\n"); + } + return sb.toString(); + } + + @Override + public String generateSeparator(final Connection conn) { + return ""; + } + + @Override + public String generateEpilog(final Connection conn, final List nodes) { + return ""; + } + + @Override + public String generate(final Connection conn, final Node node) { + return ""; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/oddgen/TestGenerator.xtend b/sqldev/src/main/java/org/utplsql/sqldev/oddgen/TestGenerator.xtend deleted file mode 100644 index 1393cc3e..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/oddgen/TestGenerator.xtend +++ /dev/null @@ -1,284 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.oddgen - -import java.io.File -import java.sql.Connection -import java.util.ArrayList -import java.util.HashMap -import java.util.LinkedHashMap -import java.util.List -import java.util.logging.Logger -import oracle.ide.config.Preferences -import org.oddgen.sqldev.generators.OddgenGenerator2 -import org.oddgen.sqldev.generators.model.Node -import org.oddgen.sqldev.generators.model.NodeTools -import org.oddgen.sqldev.plugin.templates.TemplateTools -import org.utplsql.sqldev.dal.UtplsqlDao -import org.utplsql.sqldev.model.oddgen.GenContext -import org.utplsql.sqldev.model.preference.PreferenceModel -import org.utplsql.sqldev.resources.UtplsqlResources - -class TestGenerator implements OddgenGenerator2 { - static final Logger logger = Logger.getLogger(TestGenerator.name); - - public static val YES = "Yes" - public static val NO = "No" - - public static var GENERATE_FILES = UtplsqlResources.getString("PREF_GENERATE_FILES_LABEL") - public static var OUTPUT_DIRECTORY = UtplsqlResources.getString("PREF_OUTPUT_DIRECTORY_LABEL") - public static var DELETE_EXISTING_FILES = UtplsqlResources.getString("PREF_DELETE_EXISTING_FILES_LABEL") - public static var TEST_PACKAGE_PREFIX = UtplsqlResources.getString("PREF_TEST_PACKAGE_PREFIX_LABEL") - public static var TEST_PACKAGE_SUFFIX = UtplsqlResources.getString("PREF_TEST_PACKAGE_SUFFIX_LABEL") - public static var TEST_UNIT_PREFIX = UtplsqlResources.getString("PREF_TEST_UNIT_PREFIX_LABEL") - public static var TEST_UNIT_SUFFIX = UtplsqlResources.getString("PREF_TEST_UNIT_SUFFIX_LABEL") - public static var NUMBER_OF_TESTS_PER_UNIT = UtplsqlResources.getString("PREF_NUMBER_OF_TESTS_PER_UNIT_LABEL") - public static var GENERATE_COMMENTS = UtplsqlResources.getString("PREF_GENERATE_COMMENTS_LABEL") - public static var DISABLE_TESTS = UtplsqlResources.getString("PREF_DISABLE_TESTS_LABEL") - public static var SUITE_PATH = UtplsqlResources.getString("PREF_SUITE_PATH_LABEL") - public static var INDENT_SPACES = UtplsqlResources.getString("PREF_INDENT_SPACES_LABEL") - - val extension NodeTools nodeTools = new NodeTools - val extension TemplateTools templateTools = new TemplateTools - val consoleOutput = new ArrayList(); - - private def toContext(Node node) { - val context = new GenContext() - context.objectType = node.toObjectType - context.objectName = node.toObjectName - context.testPackagePrefix = node.params.get(TEST_PACKAGE_PREFIX).toLowerCase - context.testPackageSuffix = node.params.get(TEST_PACKAGE_SUFFIX).toLowerCase - context.testUnitPrefix = node.params.get(TEST_UNIT_PREFIX).toLowerCase - context.testUnitSuffix = node.params.get(TEST_UNIT_SUFFIX).toLowerCase - context.numberOfTestsPerUnit = Integer.valueOf(node.params.get(NUMBER_OF_TESTS_PER_UNIT)) - context.generateComments = node.params.get(GENERATE_COMMENTS) == YES - context.disableTests = node.params.get(DISABLE_TESTS) == YES - context.suitePath = node.params.get(SUITE_PATH).toLowerCase - context.indentSpaces = Integer.valueOf(node.params.get(INDENT_SPACES)) - return context - } - - private def void resetConsoleOutput() { - consoleOutput.clear - } - - private def void saveConsoleOutput(String s) { - if (s !== null) { - for (line : s.split("[\\n\\r]+")) { - consoleOutput.add(line) - } - } - } - - private def void logConsoleOutput() { - for (line : consoleOutput) { - if (line.contains("error") || line.startsWith("Cannot")) { - logger.severe(line) - } else { - logger.fine(line) - } - } - } - - private def String deleteFile(File file) { - var String ret - try { - if (file.delete) { - ret = '''«file.absoluteFile» deleted.''' - } else { - ret = '''Cannot delete file «file.absoluteFile».''' - } - } catch (Exception e) { - ret = '''Cannot delete file «file.absoluteFile». Got the following error message: «e.message».''' - } - return ret - } - - private def deleteFiles(String directory) ''' - «val dir = new File(directory)» - «FOR file: dir.listFiles» - «IF !file.directory» - «IF file.name.endsWith(".pks") || file.name.endsWith(".pkb")» - «file.deleteFile» - «ENDIF» - «ENDIF» - «ENDFOR» - ''' - - override isSupported(Connection conn) { - var ret = false - if (conn !== null) { - if (conn.metaData.databaseProductName.startsWith("Oracle")) { - if (conn.metaData.databaseMajorVersion == 11) { - if (conn.metaData.databaseMinorVersion >= 2) { - ret = true - } - } else if (conn.metaData.databaseMajorVersion > 11) { - ret = true - } - } - } - return ret - } - - override getName(Connection conn) { - return "Generate test" - } - - override getDescription(Connection conn) { - return "Generates utPLSQL test packages for public units in packages, types, functions and procedures found in the current schema." - } - - override getFolders(Connection conn) { - val preferences = PreferenceModel.getInstance(Preferences.preferences) - val folders = new ArrayList - for (f : preferences.rootFolderInOddgenView.split(",").filter[!it.empty]) { - folders.add(f.trim) - } - return folders - } - - override getHelp(Connection conn) { - return "

not yet available

" - } - - override getNodes(Connection conn, String parentNodeId) { - val preferences = PreferenceModel.getInstance(Preferences.preferences) - val params = new LinkedHashMap() - params.put(GENERATE_FILES, if (preferences.generateFiles) {YES} else {NO}) - params.put(OUTPUT_DIRECTORY, preferences.outputDirectory) - params.put(DELETE_EXISTING_FILES, if (preferences.deleteExistingFiles) {YES} else {NO}) - params.put(TEST_PACKAGE_PREFIX, preferences.testPackagePrefix) - params.put(TEST_PACKAGE_SUFFIX, preferences.testPackageSuffix) - params.put(TEST_UNIT_PREFIX, preferences.testUnitPrefix) - params.put(TEST_UNIT_SUFFIX, preferences.testUnitSuffix) - params.put(NUMBER_OF_TESTS_PER_UNIT, String.valueOf(preferences.numberOfTestsPerUnit)) - params.put(GENERATE_COMMENTS, if(preferences.generateComments) {YES} else {NO}) - params.put(DISABLE_TESTS, if (preferences.disableTests) {YES} else {NO}) - params.put(SUITE_PATH, preferences.suitePath) - params.put(INDENT_SPACES, String.valueOf(preferences.indentSpaces)) - if (parentNodeId === null || parentNodeId.empty) { - val packageNode = new Node - packageNode.id = "PACKAGE" - packageNode.params = params - packageNode.leaf = false - packageNode.generatable = true - packageNode.multiselectable = true - val typeNode = new Node - typeNode.id = "TYPE" - typeNode.params = params - typeNode.leaf = false - typeNode.generatable = true - typeNode.multiselectable = true - val functionNode = new Node - functionNode.id = "FUNCTION" - functionNode.params = params - functionNode.leaf = false - functionNode.generatable = true - functionNode.multiselectable = true - val procedureNode = new Node - procedureNode.id = "PROCEDURE" - procedureNode.params = params - procedureNode.leaf = false - procedureNode.generatable = true - procedureNode.multiselectable = true - return #[packageNode, typeNode, functionNode, procedureNode] - } else { - val UtplsqlDao dao = new UtplsqlDao(conn) - val nodes = dao.testables(parentNodeId) - for (node : nodes) { - node.params = params - } - return nodes - } - } - - override getLov(Connection conn, LinkedHashMap params, List nodes) { - val lov = new HashMap>() - lov.put(NUMBER_OF_TESTS_PER_UNIT, #["1", "2", "3", "4", "5", "6", "7", "8", "9", "10"]) - lov.put(INDENT_SPACES, #["1", "2", "3", "4", "5", "6", "7", "8"]) - lov.put(GENERATE_COMMENTS, #[YES, NO]) - lov.put(DISABLE_TESTS, #[YES, NO]) - lov.put(GENERATE_FILES, #[YES, NO]) - lov.put(DELETE_EXISTING_FILES, #[YES, NO]) - return lov - } - - override getParamStates(Connection conn, LinkedHashMap params, List nodes) { - val paramStates = new HashMap - paramStates.put(OUTPUT_DIRECTORY, params.get(GENERATE_FILES) == YES) - paramStates.put(DELETE_EXISTING_FILES, params.get(GENERATE_FILES) == YES) - return paramStates - } - - override generateProlog(Connection conn, List nodes) ''' - «val generateFiles = nodes.get(0).params.get(GENERATE_FILES) == YES» - «val outputDirectory = nodes.get(0).params.get(OUTPUT_DIRECTORY)» - «val deleteExistingfiles = nodes.get(0).params.get(DELETE_EXISTING_FILES) == YES» - «IF generateFiles» - «resetConsoleOutput» - «outputDirectory.mkdirs.saveConsoleOutput» - «IF deleteExistingfiles» - «deleteFiles(outputDirectory).toString.saveConsoleOutput» - «ENDIF» - -- - -- install generated utPLSQL test packages - -- - «ENDIF» - «FOR node : nodes» - «val context = node.toContext» - «context.conn = conn» - «val testTemplate = new TestTemplate(context)» - «IF generateFiles» - «val packageName = '''«context.testPackagePrefix»«node.toObjectName»«context.testPackageSuffix»'''» - «writeToFile('''«outputDirectory»«File.separator»«packageName».pks'''.toString,testTemplate.generateSpec).saveConsoleOutput» - «writeToFile('''«outputDirectory»«File.separator»«packageName».pkb'''.toString,testTemplate.generateBody).saveConsoleOutput» - @«outputDirectory»«File.separator»«packageName».pks - @«outputDirectory»«File.separator»«packageName».pkb - «ELSE» - «testTemplate.generate» - - «ENDIF» - «ENDFOR» - «logConsoleOutput» - «IF generateFiles && consoleOutput.findFirst[it.contains("error")] !== null» - - -- - -- console output produced during the generation of this script (errors found) - -- - /* - - «FOR line : consoleOutput» - «line» - «ENDFOR» - - */ - «ENDIF» - ''' - - override generateSeparator(Connection conn) { - return "" - } - - override generateEpilog(Connection conn, List nodes) { - return "" - } - - override generate(Connection conn, Node node) { - return "" - } - -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/oddgen/TestTemplate.java b/sqldev/src/main/java/org/utplsql/sqldev/oddgen/TestTemplate.java new file mode 100644 index 00000000..036b5995 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/oddgen/TestTemplate.java @@ -0,0 +1,164 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.oddgen; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.List; + +import org.utplsql.sqldev.dal.UtplsqlDao; +import org.utplsql.sqldev.model.StringTools; +import org.utplsql.sqldev.model.oddgen.GenContext; + +public class TestTemplate { + private GenContext context; + private UtplsqlDao dao; + private List units; + private DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); + private String today = dateTimeFormatter.format(LocalDateTime.now()); + + public TestTemplate(final GenContext context) { + this.context = context; + dao = new UtplsqlDao(context.getConn()); + units = dao.units(context.getObjectType(), context.getObjectName()); + } + + public String generateSpec() { + final StringBuilder sb = new StringBuilder(); + final String objectName = context.getObjectName().toLowerCase(); + final String packageName = context.getTestPackagePrefix() + objectName + context.getTestPackageSuffix(); + sb.append("CREATE OR REPLACE PACKAGE "); + sb.append(packageName); + sb.append(" IS\n\n"); + if (context.isGenerateComments()) { + sb.append("\t/* generated by utPLSQL for SQL Developer on "); + sb.append(today); + sb.append(" */\n\n"); + } + sb.append("\t--%suite("); + sb.append(packageName); + sb.append(")\n"); + if (!context.getSuitePath().isEmpty()) { + sb.append("\t--%suitepath("); + sb.append(context.getSuitePath()); + sb.append(")\n\n"); + } + for (final String u : units) { + final String unit = u.toLowerCase(); + if (context.getNumberOfTestsPerUnit() > 1 + && ("PACKAGE".equals(context.getObjectType()) || "TYPE".equals(context.getObjectType()))) { + sb.append("\t--%context("); + sb.append(unit); + sb.append(")\n\n"); + } + for (int i=1; i <= context.getNumberOfTestsPerUnit(); i++) { + sb.append("\t--%test\n"); + if (context.isDisableTests()) { + sb.append("\t--%disabled\n"); + } + sb.append("\tPROCEDURE "); + sb.append(context.getTestUnitPrefix()); + sb.append(unit); + sb.append(context.getTestUnitSuffix()); + if (context.getNumberOfTestsPerUnit() > 1) { + sb.append(i); + } + sb.append(";\n\n"); + } + if (context.getNumberOfTestsPerUnit() > 1 + && ("PACKAGE".equals(context.getObjectType()) || "TYPE".equals(context.getObjectType()))) { + sb.append("\t--%endcontext\n\n"); + } + } + sb.append("END "); + sb.append(packageName); + sb.append(";\n"); + sb.append("/"); + final String ret = sb.toString(); + return StringTools.replaceTabsWithSpaces(ret, context.getIndentSpaces()); + } + + public String generateBody() { + StringBuilder sb = new StringBuilder(); + final String objectName = context.getObjectName().toLowerCase(); + sb.append("CREATE OR REPLACE PACKAGE BODY "); + sb.append(context.getTestPackagePrefix()); + sb.append(objectName); + sb.append(context.getTestPackageSuffix()); + sb.append(" IS\n\n"); + if (context.isGenerateComments()) { + sb.append("\t/* generated by utPLSQL for SQL Developer on "); + sb.append(today); + sb.append(" */\n\n"); + } + for (final String u : units) { + final String unit = u.toLowerCase(); + for (int i=1; i <= context.getNumberOfTestsPerUnit(); i++) { + final String procedureName = context.getTestUnitPrefix() + unit + context.getTestUnitSuffix() + + (context.getNumberOfTestsPerUnit() > 1 ? String.valueOf(i) : ""); + if (context.isGenerateComments()) { + sb.append("\t--\n"); + sb.append("\t-- test "); + sb.append(unit); + if (context.getNumberOfTestsPerUnit() > 1) { + sb.append(" case "); + sb.append(i); + sb.append(": ...\n"); + } + sb.append("\t--\n"); + } + sb.append("\tPROCEDURE "); + sb.append(procedureName); + sb.append(" IS\n"); + sb.append("\t\tl_actual INTEGER := 0;\n"); + sb.append("\t\tl_expected INTEGER := 1;\n"); + sb.append("\tBEGIN\n"); + if (context.isGenerateComments()) { + sb.append("\t\t-- populate actual\n"); + sb.append("\t\t-- "); + sb.append(objectName); + sb.append("."); + sb.append(unit); + sb.append(";\n\n"); + sb.append("\t\t-- populate expected\n"); + sb.append("\t\t-- ...\n\n"); + sb.append("\t\t-- assert\n"); + } + sb.append("\t\tut.expect(l_actual).to_equal(l_expected);\n"); + sb.append("\tEND "); + sb.append(procedureName); + sb.append(";\n\n"); + } + } + sb.append("END "); + sb.append(context.getTestPackagePrefix()); + sb.append(objectName); + sb.append(context.getTestPackageSuffix()); + sb.append(";\n"); + sb.append("/"); + final String ret = sb.toString(); + return StringTools.replaceTabsWithSpaces(ret, context.getIndentSpaces()); + } + + public CharSequence generate() { + StringBuilder sb = new StringBuilder(); + sb.append(generateSpec()); + sb.append("\n\n"); + sb.append(generateBody()); + sb.append('\n'); + return sb; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/oddgen/TestTemplate.xtend b/sqldev/src/main/java/org/utplsql/sqldev/oddgen/TestTemplate.xtend deleted file mode 100644 index 62b79555..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/oddgen/TestTemplate.xtend +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.oddgen - -import java.time.LocalDateTime -import java.time.format.DateTimeFormatter -import java.util.List -import org.utplsql.sqldev.dal.UtplsqlDao -import org.utplsql.sqldev.model.oddgen.GenContext - -class TestTemplate { - var GenContext context - var UtplsqlDao dao - var List units - var dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); - var today = dateTimeFormatter.format(LocalDateTime.now()) - - new(GenContext context) { - this.context = context - dao = new UtplsqlDao(context.conn) - units = dao.units(context.objectType, context.objectName) - } - - def replaceTabsWithSpaces(CharSequence input) { - val spaces = String.format("%1$"+context.indentSpaces+"s", "") - return input.toString.replace("\t", spaces) - } - - def generateSpec() { - val ret = ''' - «val objectName = context.objectName.toLowerCase» - «val packageName = '''«context.testPackagePrefix»«objectName»«context.testPackageSuffix»'''» - CREATE OR REPLACE PACKAGE «packageName» IS - - «IF context.generateComments» - /* generated by utPLSQL for SQL Developer on «today» */ - - «ENDIF» - --%suite(«packageName») - «IF !context.suitePath.empty» - --%suitepath(«context.suitePath») - «ENDIF» - - «FOR u : units» - «val unit = u.toLowerCase» - «IF context.numberOfTestsPerUnit > 1 && (context.objectType == "PACKAGE" || context.objectType == "TYPE")» - --%context(«unit») - - «ENDIF» - «FOR i : 1 .. context.numberOfTestsPerUnit» - --%test - «IF context.disableTests» - --%disabled - «ENDIF» - PROCEDURE «context.testUnitPrefix»«unit»«context.testUnitSuffix»«IF context.numberOfTestsPerUnit > 1»«i»«ENDIF»; - - «ENDFOR» - «IF context.numberOfTestsPerUnit > 1 && (context.objectType == "PACKAGE" || context.objectType == "TYPE")» - --%endcontext - - «ENDIF» - «ENDFOR» - END «packageName»; - / - ''' - return ret.replaceTabsWithSpaces - } - - def generateBody() { - val ret = ''' - «val objectName = context.objectName.toLowerCase» - CREATE OR REPLACE PACKAGE BODY «context.testPackagePrefix»«objectName»«context.testPackageSuffix» IS - - «IF context.generateComments» - /* generated by utPLSQL for SQL Developer on «today» */ - - «ENDIF» - «FOR u : units» - «val unit = u.toLowerCase» - «FOR i : 1 .. context.numberOfTestsPerUnit» - «val procedureName = '''«context.testUnitPrefix»«unit»«context.testUnitSuffix»«IF context.numberOfTestsPerUnit > 1»«i»«ENDIF»'''» - «IF context.generateComments» - -- - -- test «unit»«IF context.numberOfTestsPerUnit > 0» case «i»: ...«ENDIF» - -- - «ENDIF» - PROCEDURE «procedureName» IS - l_actual INTEGER := 0; - l_expected INTEGER := 1; - BEGIN - «IF context.generateComments» - -- populate actual - -- «objectName».«unit»; - - -- populate expected - -- ... - - -- assert - «ENDIF» - ut.expect(l_actual).to_equal(l_expected); - END «procedureName»; - - «ENDFOR» - «ENDFOR» - END «context.testPackagePrefix»«objectName»«context.testPackageSuffix»; - / - ''' - return ret.replaceTabsWithSpaces - } - - def generate() ''' - «generateSpec» - - «generateBody» - ''' -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/parser/SqlDevParser.java b/sqldev/src/main/java/org/utplsql/sqldev/parser/SqlDevParser.java new file mode 100644 index 00000000..4bda72a6 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/parser/SqlDevParser.java @@ -0,0 +1,68 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.parser; + +import java.util.List; +import java.util.Optional; +import java.util.Set; + +import oracle.dbtools.parser.LexerToken; +import oracle.dbtools.raptor.navigator.plsql.Member; +import oracle.dbtools.raptor.navigator.plsql.PlSqlArguments; +import oracle.dbtools.raptor.navigator.plsql.PlsqlStructureParser; + +/* + * Cannot use this class within SQL Developer because the + * package oracle.dbtools.parser is not exported in sqldeveloper OSGI bundle (extension) + * (throws ClassNotFoundException at runtime). + * + * The dbtools-common.jar contains the necessary packages, + * but it cannot be distributed with the utPLSQL extension + * without violating the Oracle license agreement. + */ +public class SqlDevParser { + + @SuppressWarnings("unchecked") + public Set getMembers(final String plsql) { + final List tokens = LexerToken.parse(plsql); + final PlsqlStructureParser parser = new PlsqlStructureParser(); + parser.parse(tokens, PlSqlArguments.getSort()); + return parser.children; + } + + private int getStartLine(final String plsql, final int offset) { + int line = 1; + for (int i = 0; i < plsql.length(); i++) { + final String c = plsql.substring(i, i + 1); + if (i > offset) { + return line; + } else if ("\n".equals(c)) { + line = line + 1; + } + } + return line; + } + + public int getMemberStartLine(final String plsql, final String memberName) { + final Set members = this.getMembers(plsql); + final Optional member = members.stream().filter(it -> it.name.equalsIgnoreCase(memberName)).findFirst(); + if (member.isPresent()) { + return this.getStartLine(plsql, member.get().codeOffset); + } else { + return 1; + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/parser/SqlDevParser.xtend b/sqldev/src/main/java/org/utplsql/sqldev/parser/SqlDevParser.xtend deleted file mode 100644 index 3eb38765..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/parser/SqlDevParser.xtend +++ /dev/null @@ -1,60 +0,0 @@ -/* Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.parser - -import oracle.dbtools.parser.LexerToken -import oracle.dbtools.raptor.navigator.plsql.PlSqlArguments -import oracle.dbtools.raptor.navigator.plsql.PlsqlStructureParser - -/* - * Cannot use this class within SQL Developer because the - * package oracle.dbtools.parser is not exported in sqldeveloper OSGI bundle (extension) - * (throws ClassNotFoundException at runtime). - * - * The dbtools-common.jar contains the necessary packages, - * but it cannot be distributed with the utPLSQL extension - * without violating the Oracle license agreement. - */ -class SqlDevParser { - def getMembers(String plsql) { - val tokens = LexerToken.parse(plsql) - val parser = new PlsqlStructureParser - parser.parse(tokens, PlSqlArguments.sort) - return parser.children - } - - private def getStartLine(String plsql, int offset) { - var int line = 1 - for (var i = 0; i < plsql.length; i++) { - val c = plsql.substring(i, i+1) - if (i > offset) { - return line - } else if (c == '\n') { - line = line + 1 - } - } - return line - } - - def getMemberStartLine(String plsql, String memberName) { - val members = plsql.members - val member = members.findFirst[it.name.equalsIgnoreCase(memberName)] - if (member !== null) { - return getStartLine(plsql, member.codeOffset) - } else { - 1 - } - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/parser/UtplsqlParser.java b/sqldev/src/main/java/org/utplsql/sqldev/parser/UtplsqlParser.java new file mode 100644 index 00000000..8eb5c9ef --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/parser/UtplsqlParser.java @@ -0,0 +1,280 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.parser; + +import java.sql.Connection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.swing.text.JTextComponent; + +import org.utplsql.sqldev.dal.UtplsqlDao; +import org.utplsql.sqldev.exception.GenericRuntimeException; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.parser.PlsqlObject; +import org.utplsql.sqldev.model.parser.Unit; +import org.utplsql.sqldev.model.ut.Annotation; + +public class UtplsqlParser { + private String owner; + private String plsql; + private String plsqlReduced; + private ArrayList objects = new ArrayList<>(); + private ArrayList units = new ArrayList<>(); + + public UtplsqlParser(final String plsql, final Connection conn, final String owner) { + setPlsql(plsql); + setPlsqlReduced(); + populateObjects(); + populateUnits(); + processAnnotations(conn, owner); + } + + public UtplsqlParser(final String plsql) { + this(plsql, null, null); + } + + /** + * JTextComponents uses one position for EOL (end-of-line), + * even on Windows platforms were it is two characters (CR/LF). + * To simplify position calculations and subsequent regular expressions + * all new lines are replaced with LF on Windows platforms. + */ + private void setPlsql(final String plsql) { + final String lineSep = System.getProperty("line.separator"); + if (!"\n".equals(lineSep)) { + // replace CR/LF with LF on Windows platforms + this.plsql = plsql.replace(lineSep, "\n"); + } else { + this.plsql = plsql; + } + } + + /** + * replace the following expressions with space to simplify + * and improve performance of subsequent regular expressions: + * - multi-line PL/SQL comments + * - single-line PL/SQL comments + * - string literals + * the result is not valid PL/SQL anymore, but good enough + * to find PL/SQL objects and units + */ + private void setPlsqlReduced() { + final StringBuilder sb = new StringBuilder(); + final Pattern p = Pattern.compile("(/\\*(.|[\\n])*?\\*/)|(--[^\\n]*\\n)|(\'([^\']|[\\n])*?\')"); + final Matcher m = p.matcher(plsql); + int pos = 0; + while (m.find()) { + if (pos < m.start()) { + sb.append(plsql.substring(pos, m.start())); + } + for (int i = m.start(); i < m.end(); i++) { + final String c = plsql.substring(i, i + 1); + if ("\n".equals(c) || "\r".equals(c)) { + sb.append(c); + } else { + sb.append(' '); + } + } + pos = m.end(); + } + if (plsql.length() > pos) { + sb.append(plsql.substring(pos, plsql.length())); + } + plsqlReduced=sb.toString(); + } + + private void populateObjects() { + final Pattern p = Pattern.compile( + "(?i)(\\s*)(create(\\s+or\\s+replace)?\\s+(package|type|function|procedure)\\s+(body\\s+)?)([^\\s]+)(\\s+)"); + final Matcher m = p.matcher(plsqlReduced); + while (m.find()) { + final PlsqlObject o = new PlsqlObject(); + o.setType(m.group(4).toUpperCase()); + o.setName(m.group(6)); + o.setPosition(m.start()); + objects.add(o); + } + } + + private void populateUnits() { + final Pattern p = Pattern.compile("(?i)(\\s*)(procedure)(\\s+)([^\\s\\(;]+)"); + final Matcher m = p.matcher(plsqlReduced); + while (m.find()) { + final Unit u = new Unit(); + u.setName(m.group(4)); + u.setPosition(m.start()); + u.setPositionOfName(m.start(4)); + units.add(u); + } + } + + private void processAnnotations(final Connection conn, final String owner) { + this.owner = owner; + if (conn != null) { + final UtplsqlDao dao = new UtplsqlDao(conn); + if (dao.isUtAnnotationManagerInstalled()) { + for (final PlsqlObject o : objects) { + final List segments = Arrays.asList(fixName(o.getName()).split("\\.")); + final String schema = owner != null ? owner : DatabaseTools.getSchema(conn); + final List annotations = dao.annotations(schema, + segments.get(segments.size() - 1).toUpperCase()); + if (annotations.stream().anyMatch(it -> it.getName().equals("suite"))) { + o.setAnnotations(annotations); + } + } + final ArrayList fixedUnits = new ArrayList<>(); + for (final Unit u : units) { + final PlsqlObject o = getObjectAt(u.getPosition()); + if (o != null && o.getAnnotations() != null + && o.getAnnotations().stream().anyMatch(it -> "test".equals(it.getName()) + && it.getSubobjectName().equalsIgnoreCase(fixName(u.getName())))) { + fixedUnits.add(u); + } + } + units = fixedUnits; + final ArrayList fixedObjects = new ArrayList<>(); + for (final PlsqlObject o : objects) { + if (o.getAnnotations() != null) { + fixedObjects.add(o); + } + } + objects = fixedObjects; + } + } + } + + /** + * gets the PL/SQL object based on the current editor position + * + * @param position + * the absolute position as used in + * {@link JTextComponent#getCaretPosition()} + * @return the PL/SQL object + */ + public PlsqlObject getObjectAt(final int position) { + PlsqlObject obj = null; + for (final PlsqlObject o : objects) { + if (o.getPosition() <= position) { + obj = o; + } + } + return obj; + } + + /** + * converts a line and column to a postion as used in as used in + * {@link JTextComponent#getCaretPosition()} used for testing purposes only + * + * @param line + * the line as used in SQL Developer, starting with 1 + * @param column + * the column as used in SQL Developer, starting with 1 + * @return the position + */ + public int toPosition(final int line, final int column) { + int lines = 0; + for (int i = 0; i < plsql.length(); i++) { + if ("\n".equals(plsql.substring(i, i + 1))) { + lines++; + if (lines == line - 1) { + return i + column; + } + } + } + throw new GenericRuntimeException("Line " + line + " not found."); + } + + private String getUnitNameAt(final int position) { + String name = ""; + for (final Unit u : units) { + if (u.getPosition() <= position) { + name = u.getName(); + } + } + return name; + } + + private String fixName(final String name) { + return name.replace("\"", ""); + } + + public List getObjects() { + return objects; + } + + public List getUnits() { + return units; + } + + /** + * gets the utPLSQL path based on the current editor position + * + * @param position + * the absolute position as used in + * {@link JTextComponent#getCaretPosition()} + * @return the utPLSQL path + */ + public String getPathAt(final int position) { + final StringBuilder sb = new StringBuilder(); + final PlsqlObject object = getObjectAt(position); + if (object != null && "PACKAGE".equals(object.getType())) { + final String unitName = getUnitNameAt(position); + if (owner != null) { + sb.append(owner); + sb.append("."); + } + sb.append(fixName(object.getName())); + if (!unitName.isEmpty()) { + sb.append("."); + sb.append(fixName(unitName)); + } + } + return sb.toString(); + } + + private int getStartLine(final int position) { + int line = 1; + for (int i = 0; i < plsql.length(); i++) { + final String c = plsql.substring(i, i + 1); + if (i > position) { + return line; + } else if ("\n".equals(c)) { + line++; + } + } + return line; + } + + /** + * get the line of a PL/SQL package unit + * + * @param unitName + * name of the unit. Only procedures are supported + * @return the line where the procedure is defined + */ + public int getLineOf(final String unitName) { + for (final Unit u : units) { + if (u.getName().equalsIgnoreCase(unitName)) { + return getStartLine(u.getPositionOfName()); + } + } + return 1; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/parser/UtplsqlParser.xtend b/sqldev/src/main/java/org/utplsql/sqldev/parser/UtplsqlParser.xtend deleted file mode 100644 index 736be98e..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/parser/UtplsqlParser.xtend +++ /dev/null @@ -1,253 +0,0 @@ -/* Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.parser - -import java.sql.Connection -import java.util.ArrayList -import java.util.Arrays -import java.util.regex.Pattern -import javax.swing.text.JTextComponent -import org.utplsql.sqldev.dal.UtplsqlDao -import org.utplsql.sqldev.model.parser.PlsqlObject -import org.utplsql.sqldev.model.parser.Unit - -class UtplsqlParser { - String owner - String plsql - String plsqlReduced - ArrayList objects = new ArrayList - ArrayList units = new ArrayList - - new(String plsql, Connection conn, String owner) { - setPlsql(plsql) - setPlsqlReduced - populateObjects - populateUnits - processAnnotations(conn, owner) - } - - new(String plsql) { - this(plsql, null, null) - } - - /** - * JTextComponents uses one position for EOL (end-of-line), - * even on Windows platforms were it is two characters (CR/LF). - * To simplify position calculations and subsequent regular expressions - * all new lines are replaced with LF on Windows platforms. - */ - private def setPlsql(String plsql) { - val lineSep = System.getProperty("line.separator") - if (lineSep.length > 0) { - // replace CR/LF with LF on Windows platforms - this.plsql = plsql.replace(lineSep, "\n") - } else { - this.plsql = plsql - } - } - - /** - * replace the following expressions with space to simplify - * and improve performance of subsequent regular expressions: - * - multi-line PL/SQL comments - * - single-line PL/SQL comments - * - string literals - * the result is not valid PL/SQL anymore, but good enough - * to find PL/SQL objects and units - */ - private def setPlsqlReduced() { - val sb = new StringBuffer - val p = Pattern.compile("(/\\*(.|[\\n])*?\\*/)|(--[^\\n]*\\n)|('([^']|[\\n])*?')") - val m = p.matcher(plsql) - var pos = 0 - while (m.find) { - if (pos < m.start) { - sb.append(plsql.substring(pos, m.start)) - } - for (var i=m.start; i pos) { - sb.append(plsql.substring(pos, plsql.length)) - } - plsqlReduced=sb.toString - } - - private def populateObjects() { - val p = Pattern.compile("(?i)(\\s*)(create(\\s+or\\s+replace)?\\s+(package|type|function|procedure)\\s+(body\\s+)?)([^\\s]+)(\\s+)") - val m = p.matcher(plsqlReduced) - while (m.find) { - val o = new PlsqlObject - o.type = m.group(4).toUpperCase - o.name = m.group(6) - o.position = m.start - objects.add(o) - } - } - private def populateUnits() { - val p = Pattern.compile("(?i)(\\s*)(procedure)(\\s+)([^\\s\\(;]+)") - val m = p.matcher(plsqlReduced) - while (m.find) { - val u = new Unit - u.name = m.group(4) - u.position = m.start - u.positionOfName = m.start(4) - units.add(u) - } - } - - private def processAnnotations(Connection conn, String owner) { - this.owner = owner - if (conn !== null) { - val dao = new UtplsqlDao(conn) - if (dao.utAnnotationManagerInstalled) { - for (o : objects) { - val segments = Arrays.asList(o.name.fixName.split("\\.")) - val annotations = dao.annotations(if (owner !== null) {owner} else {conn.schema}, segments.last.toUpperCase) - if (annotations.findFirst[it.name == "suite"] !== null) { - o.annotations = annotations - } - } - val fixedUnits = new ArrayList - for (u : units) { - val o = getObjectAt(u.position) - if (o?.annotations !== null && o.annotations.findFirst [ - it.name == "test" && it.subobjectName.equalsIgnoreCase(u.name.fixName) - ] !== null) { - fixedUnits.add(u) - } - } - units = fixedUnits - val fixedObjects = new ArrayList - for (o : objects) { - if (o.annotations !== null) { - fixedObjects.add(o) - } - } - objects = fixedObjects - } - } - } - - /** - * gets the PL/SQL object based on the current editor position - * - * @param position the absolute position as used in {@link JTextComponent#getCaretPosition()} - * @return the PL/SQL object - */ - def getObjectAt(int position) { - var PlsqlObject obj - for (o : objects) { - if (o.position <= position) { - obj = o - } - } - return obj - } - - /** - * converts a line and column to a postion as used in as used in {@link JTextComponent#getCaretPosition()} - * used for testing purposes only - * - * @param line the line as used in SQL Developer, starting with 1 - * @param column the column as used in SQL Developer, starting with 1 - * @return the position - */ - def toPosition(int line, int column) { - var lines=0 - for (var i=0; i position) { - return line - } else if (c == '\n') { - line = line + 1 - } - } - return line - } - - /** - * get the line of a PL/SQL package unit - * - * @param unitName name of the unit. Only procedures are supported - * @return the line where the procedure is defined - */ - def getLineOf(String unitName) { - for (u : units) { - if (u.name.equalsIgnoreCase(unitName)) { - return u.positionOfName.startLine - } - } - return 1 - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/resources/UtplsqlResources.java b/sqldev/src/main/java/org/utplsql/sqldev/resources/UtplsqlResources.java new file mode 100644 index 00000000..1bd9936e --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/resources/UtplsqlResources.java @@ -0,0 +1,56 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.resources; + +import java.awt.Image; + +import javax.swing.Icon; + +import oracle.dbtools.raptor.utils.MessagesBase; + +public class UtplsqlResources extends MessagesBase { + private static final ClassLoader CLASS_LOADER = UtplsqlResources.class.getClassLoader(); + private static final String CLASS_NAME = UtplsqlResources.class.getCanonicalName(); + private static final UtplsqlResources INSTANCE = new UtplsqlResources(); + + private UtplsqlResources() { + super(CLASS_NAME, CLASS_LOADER); + } + + public static String getString(final String paramString) { + return INSTANCE.getStringImpl(paramString); + } + + public static String get(final String paramString) { + return getString(paramString); + } + + public static Image getImage(final String paramString) { + return INSTANCE.getImageImpl(paramString); + } + + public static String format(final String paramString, final Object... paramVarArgs) { + return INSTANCE.formatImpl(paramString, paramVarArgs); + } + + public static Icon getIcon(final String paramString) { + return INSTANCE.getIconImpl(paramString); + } + + public static Integer getInteger(final String paramString) { + return INSTANCE.getIntegerImpl(paramString); + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/resources/UtplsqlResources.xtend b/sqldev/src/main/java/org/utplsql/sqldev/resources/UtplsqlResources.xtend deleted file mode 100644 index 07c2534b..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/resources/UtplsqlResources.xtend +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.resources - -import oracle.dbtools.raptor.utils.MessagesBase - -class UtplsqlResources extends MessagesBase { - static final ClassLoader CLASS_LOADER = UtplsqlResources.classLoader - static final String CLASS_NAME = UtplsqlResources.canonicalName - static final UtplsqlResources INSTANCE = new UtplsqlResources() - - private new() { - super(CLASS_NAME, CLASS_LOADER) - } - - def static getString(String paramString) { - return INSTANCE.getStringImpl(paramString) - } - - def static get(String paramString) { - return getString(paramString) - } - - def static getImage(String paramString) { - return INSTANCE.getImageImpl(paramString) - } - - def static format(String paramString, Object... paramVarArgs) { - return INSTANCE.formatImpl(paramString, paramVarArgs) - } - - def static getIcon(String paramString) { - return INSTANCE.getIconImpl(paramString) - } - - def static getInteger(String paramString) { - return INSTANCE.getIntegerImpl(paramString) - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlRunner.java b/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlRunner.java new file mode 100644 index 00000000..bf0628af --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlRunner.java @@ -0,0 +1,327 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.runner; + +import java.awt.Dimension; +import java.awt.Toolkit; +import java.sql.Connection; +import java.text.SimpleDateFormat; +import java.util.Arrays; +import java.util.Date; +import java.util.List; +import java.util.UUID; +import java.util.logging.Logger; + +import javax.swing.JFrame; + +import org.utplsql.sqldev.dal.RealtimeReporterDao; +import org.utplsql.sqldev.dal.RealtimeReporterEventConsumer; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.SystemTools; +import org.utplsql.sqldev.model.runner.PostRunEvent; +import org.utplsql.sqldev.model.runner.PostSuiteEvent; +import org.utplsql.sqldev.model.runner.PostTestEvent; +import org.utplsql.sqldev.model.runner.PreRunEvent; +import org.utplsql.sqldev.model.runner.PreSuiteEvent; +import org.utplsql.sqldev.model.runner.PreTestEvent; +import org.utplsql.sqldev.model.runner.RealtimeReporterEvent; +import org.utplsql.sqldev.model.runner.Run; +import org.utplsql.sqldev.model.runner.Test; +import org.utplsql.sqldev.resources.UtplsqlResources; +import org.utplsql.sqldev.ui.runner.RunnerFactory; +import org.utplsql.sqldev.ui.runner.RunnerPanel; +import org.utplsql.sqldev.ui.runner.RunnerView; + +public class UtplsqlRunner implements RealtimeReporterEventConsumer { + private static final Logger logger = Logger.getLogger(UtplsqlRunner.class.getName()); + + private List pathList; + private String connectionName; + private Connection producerConn; + private Connection consumerConn; + private final String reporterId = UUID.randomUUID().toString().replace("-", ""); + private Run run; + private RunnerPanel panel; + private Thread producerThread; + private Thread consumerThread; + + public UtplsqlRunner(final List pathList, final String connectionName) { + this.pathList = pathList; + setConnection(connectionName); + } + + /** + * this constructor is intended for tests only + */ + public UtplsqlRunner(final List pathList, final Connection producerConn, final Connection consumerConn) { + this.pathList = pathList; + this.producerConn = producerConn; + this.consumerConn = consumerConn; + } + + private void setConnection(final String connectionName) { + if (connectionName == null) { + throw new NullPointerException("Cannot initialize a RealtimeConsumer without a ConnectionName"); + } else { + producerConn = DatabaseTools.cloneConnection(connectionName); + consumerConn = DatabaseTools.cloneConnection(connectionName); + } + this.connectionName = connectionName; + } + + public void dispose() { + // running in SQL Developer + DatabaseTools.closeConnection(producerConn); + DatabaseTools.closeConnection(consumerConn); + } + + @Override + public void process(final RealtimeReporterEvent event) { + logger.fine(() -> event.toString()); + // dynamic dispatching code originally generated by Xtend + if (event instanceof PostRunEvent) { + doProcess((PostRunEvent) event); + } else if (event instanceof PostSuiteEvent) { + doProcess((PostSuiteEvent) event); + } else if (event instanceof PostTestEvent) { + doProcess((PostTestEvent) event); + } else if (event instanceof PreRunEvent) { + doProcess((PreRunEvent) event); + } else if (event instanceof PreSuiteEvent) { + doProcess((PreSuiteEvent) event); + } else if (event instanceof PreTestEvent) { + doProcess((PreTestEvent) event); + } else { + throw new IllegalArgumentException("Unhandled parameter types: " + Arrays.asList(event).toString()); + } + } + + private String getSysdate() { + final Date dateTime = new Date(System.currentTimeMillis()); + final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'000'"); + return df.format(dateTime); + } + + private void initRun() { + run = new Run(reporterId, connectionName, pathList); + run.setStartTime(getSysdate()); + run.getCounter().setDisabled(0); + run.getCounter().setSuccess(0); + run.getCounter().setFailure(0); + run.getCounter().setError(0); + run.getCounter().setWarning(0); + run.setInfoCount(0); + run.setTotalNumberOfTests(-1); + run.setCurrentTestNumber(0); + run.setStatus(UtplsqlResources.getString("RUNNER_INITIALIZING_TEXT")); + panel.setModel(run); + panel.update(reporterId); + } + + private void doProcess(final PreRunEvent event) { + run.setTotalNumberOfTests(event.getTotalNumberOfTests()); + run.put(event.getItems()); + run.setStatus(UtplsqlResources.getString("RUNNER_RUNNING_TEXT")); + panel.update(reporterId); + } + + private void doProcess(final PostRunEvent event) { + run.setStartTime(event.getStartTime()); + run.setEndTime(event.getEndTime()); + run.setExecutionTime(event.getExecutionTime()); + run.setErrorStack(event.getErrorStack()); + run.setServerOutput(event.getServerOutput()); + run.setStatus(UtplsqlResources.getString("RUNNER_FINNISHED_TEXT")); + panel.update(reporterId); + } + + private void doProcess(final PreSuiteEvent event) { + // ignore + } + + private void doProcess(final PostSuiteEvent event) { + final Test test = run.getCurrentTest(); + // Errors on suite levels are reported as warnings by the utPLSQL framework, + // since an error on suite level does not affect a status of a test. + // It is possible that the test is OK, but contains error messages on suite level(s) + // Populating test.errorStack would be a) wrong and b) redundant + if (event.getWarnings() != null) { + if (test.getCounter().getWarning() == 0) { + test.getCounter().setWarning(1); + test.getCounter().setWarning(run.getCounter().getWarning() + 1); + } + StringBuilder sb = new StringBuilder(); + if (test.getWarnings() != null) { + sb.append(test.getWarnings()); + sb.append("\n\n"); + } + sb.append("For suite "); + sb.append(event.getId()); + sb.append(":\n\n"); + sb.append(event.getWarnings()); + test.setWarnings(sb.toString()); + } + if (event.getServerOutput() != null) { + if (test.getServerOutput() == null) { + run.setInfoCount(run.getInfoCount() + 1); + } + StringBuilder sb = new StringBuilder(); + if (test.getServerOutput() != null) { + sb.append(test.getServerOutput()); + sb.append("\n\n"); + } + sb.append("For suite "); + sb.append(event.getId()); + sb.append(":\n\n"); + sb.append(event.getServerOutput()); + test.setServerOutput(sb.toString()); + } + panel.update(reporterId); + } + + private void doProcess(final PreTestEvent event) { + final Test test = run.getTest(event.getId()); + if (test == null) { + logger.severe(() -> "Could not find test id \"" + event.getId() + "\" when processing PreTestEvent " + + event.toString() + "."); + } else { + test.setStartTime(getSysdate()); + } + run.setStatus(event.getId() + "..."); + run.setCurrentTestNumber(event.getTestNumber()); + run.setCurrentTest(test); + panel.update(reporterId); + } + + private void doProcess(final PostTestEvent event) { + final Test test = run.getTest(event.getId()); + if (test == null) { + logger.severe(() -> "Could not find test id \"" + event.getId() + "\" when processing PostTestEvent " + + event.toString() + "."); + } else { + test.setStartTime(event.getStartTime()); + test.setEndTime(event.getEndTime()); + test.setExecutionTime(event.getExecutionTime()); + test.setCounter(event.getCounter()); + test.setErrorStack(event.getErrorStack()); + test.setServerOutput(event.getServerOutput()); + if (test.getServerOutput() != null) { + run.setInfoCount(run.getInfoCount() + 1); + } + test.setFailedExpectations(event.getFailedExpectations()); + test.setWarnings(event.getWarnings()); + if (test.getWarnings() != null) { + test.getCounter().setWarning(1); + } else { + test.getCounter().setWarning(0); + } + run.getCounter().setWarning(run.getCounter().getWarning() + test.getCounter().getWarning()); + } + run.getCounter().setDisabled(run.getCounter().getDisabled() + event.getCounter().getDisabled()); + run.getCounter().setSuccess(run.getCounter().getSuccess() + event.getCounter().getSuccess()); + run.getCounter().setFailure(run.getCounter().getFailure() + event.getCounter().getFailure()); + run.getCounter().setError(run.getCounter().getError() + event.getCounter().getError()); + panel.update(reporterId); + } + + private void produce() { + try { + logger.fine(() -> "Running utPLSQL tests and producing events via reporter id " + reporterId + "..."); + final RealtimeReporterDao dao = new RealtimeReporterDao(producerConn); + dao.produceReport(reporterId, pathList); + logger.fine(() -> "All events produced for reporter id " + reporterId + "."); + } catch (Exception e) { + logger.severe(() -> "Error while producing events for reporter id " + reporterId + ": " + + (e != null ? e.getMessage() : "???")); + } + } + + private void consume() { + try { + logger.fine(() -> "Consuming events from reporter id " + reporterId + " in realtime..."); + final RealtimeReporterDao dao = new RealtimeReporterDao(consumerConn); + dao.consumeReport(reporterId, this); + logger.fine(() -> "All events consumed."); + } catch (Exception e) { + logger.severe(() -> "Error while consuming events for reporter id " + reporterId + ": " + + (e != null ? e.getMessage() : "???")); + } + if (run.getTotalNumberOfTests() < 0) { + run.setStatus(UtplsqlResources.getString("RUNNER_NO_TESTS_FOUND_TEXT")); + run.setExecutionTime(Double.valueOf(System.currentTimeMillis() - Double.valueOf(run.getStart())) / 1000); + run.setEndTime(getSysdate()); + run.setTotalNumberOfTests(0); + panel.update(reporterId); + } + if (isRunningInSqlDeveloper()) { + dispose(); + } + } + + private boolean isRunningInSqlDeveloper() { + return (connectionName != null); + } + + private boolean initGUI() { + RunnerView dockable = null; + if (isRunningInSqlDeveloper() && (dockable = RunnerFactory.getDockable()) == null) { + logger.severe(() -> "Error getting utPLSQL dockable. Cannot run utPLSQL test."); + return false; + } else { + if (isRunningInSqlDeveloper() && dockable != null) { + RunnerFactory.showDockable(); + panel = dockable.getRunnerPanel(); + } else { + final JFrame frame = new JFrame("utPLSQL Runner Panel"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + panel = new RunnerPanel(); + frame.add(panel.getGUI()); + frame.setPreferredSize(new Dimension(600, 800)); + frame.pack(); + final Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); + frame.setLocation(dim.width / 2 - frame.getSize().width / 2, + dim.height / 2 - frame.getSize().height / 2); + frame.setVisible(true); + } + initRun(); + return true; + } + } + + public void runTestAsync() { + // start tests when the GUI has been successfully initialized. + if (initGUI()) { + // the consumer + consumerThread = new Thread(() -> consume()); + consumerThread.setName("realtime consumer"); + consumerThread.start(); + // avoid concurrency on output header table to fix issue #80 + SystemTools.sleep(100); + // the producer + producerThread = new Thread(() -> produce()); + producerThread.setName("realtime producer"); + producerThread.start(); + } + } + + public Thread getProducerThread() { + return producerThread; + } + + public Thread getConsumerThread() { + return consumerThread; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlRunner.xtend b/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlRunner.xtend deleted file mode 100644 index 23eec363..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlRunner.xtend +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.runner - -import java.awt.Dimension -import java.awt.Toolkit -import java.sql.Connection -import java.text.SimpleDateFormat -import java.util.Date -import java.util.List -import java.util.UUID -import java.util.logging.Logger -import javax.swing.JFrame -import oracle.dbtools.raptor.utils.Connections -import org.utplsql.sqldev.dal.RealtimeReporterDao -import org.utplsql.sqldev.dal.RealtimeReporterEventConsumer -import org.utplsql.sqldev.model.runner.PostRunEvent -import org.utplsql.sqldev.model.runner.PostSuiteEvent -import org.utplsql.sqldev.model.runner.PostTestEvent -import org.utplsql.sqldev.model.runner.PreRunEvent -import org.utplsql.sqldev.model.runner.PreSuiteEvent -import org.utplsql.sqldev.model.runner.PreTestEvent -import org.utplsql.sqldev.model.runner.RealtimeReporterEvent -import org.utplsql.sqldev.model.runner.Run -import org.utplsql.sqldev.resources.UtplsqlResources -import org.utplsql.sqldev.ui.runner.RunnerFactory -import org.utplsql.sqldev.ui.runner.RunnerPanel -import org.utplsql.sqldev.ui.runner.RunnerView - -class UtplsqlRunner implements RealtimeReporterEventConsumer { - - static val Logger logger = Logger.getLogger(UtplsqlRunner.name); - - var List pathList - var String connectionName - var Connection producerConn - var Connection consumerConn - val String reporterId = UUID.randomUUID().toString.replace("-", "") - var Run run - var RunnerPanel panel - var Thread producerThread - var Thread consumerThread - - new(List pathList, String connectionName) { - this.pathList = pathList - setConnection(connectionName) - } - - /** - * this constructor is intended for tests only - */ - new(List pathList, Connection producerConn, Connection consumerConn) { - this.pathList = pathList - this.producerConn = producerConn - this.consumerConn = consumerConn - } - - private def setConnection(String connectionName) { - if (connectionName === null) { - throw new RuntimeException("Cannot initialize a RealtimeConsumer without a ConnectionName") - } else { - this.producerConn = Connections.instance.cloneConnection(Connections.instance.getConnection(connectionName)) - this.consumerConn = Connections.instance.cloneConnection(Connections.instance.getConnection(connectionName)) - } - this.connectionName = connectionName - } - - def dispose() { - // running in SQL Developer - if (!producerConn.closed) { - producerConn.close; - } - if (!consumerConn.closed) { - consumerConn.close; - } - } - - override void process(RealtimeReporterEvent event) { - logger.fine(event.toString) - event.doProcess - } - - private def getSysdate() { - val dateTime = new Date(System.currentTimeMillis); - val df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'000'"); - return df.format(dateTime) - - } - - private def initRun() { - run = new Run(reporterId, connectionName, pathList) - run.startTime = sysdate - run.counter.disabled = 0 - run.counter.success = 0 - run.counter.failure = 0 - run.counter.error = 0 - run.counter.warning = 0 - run.infoCount = 0 - run.totalNumberOfTests = -1 - run.currentTestNumber = 0 - run.status = UtplsqlResources.getString("RUNNER_INITIALIZING_TEXT") - panel.model = run - panel.update(reporterId) - } - - private def dispatch doProcess(PreRunEvent event) { - run.totalNumberOfTests = event.totalNumberOfTests - run.put(event.items) - run.status = UtplsqlResources.getString("RUNNER_RUNNING_TEXT") - panel.update(reporterId) - } - - private def dispatch doProcess(PostRunEvent event) { - run.startTime = event.startTime - run.endTime = event.endTime - run.executionTime = event.executionTime - run.errorStack = event.errorStack - run.serverOutput = event.serverOutput - run.status = UtplsqlResources.getString("RUNNER_FINNISHED_TEXT") - panel.update(reporterId) - } - - private def dispatch doProcess(PreSuiteEvent event) { - // ignore - } - - private def dispatch doProcess(PostSuiteEvent event) { - val test = run.currentTest - // Errors on suite levels are reported as warnings by the utPLSQL framework, - // since an error on suite level does not affect a status of a test. - // It is possible that the test is OK, but contains error messages on suite level(s) - // Populating test.errorStack would be a) wrong and b) redundant - if (event.warnings !== null) { - if (test.counter.warning == 0) { - test.counter.warning = 1 - run.counter.warning = run.counter.warning + 1 - } - test.warnings = ''' - «IF test.warnings !== null» - «test.warnings» - - «ENDIF» - For suite «event.id»: - - «event.warnings» - '''.toString.trim - } - if (event.serverOutput !== null) { - if (test.serverOutput === null) { - run.infoCount = run.infoCount + 1 - } - test.serverOutput = ''' - «IF test.serverOutput !== null» - «test.serverOutput» - - «ENDIF» - For suite «event.id»: - - «event.serverOutput» - '''.toString.trim - } - panel.update(reporterId) - } - - private def dispatch doProcess(PreTestEvent event) { - val test = run.getTest(event.id) - if (test === null) { - logger.severe('''Could not find test id "«event.id»" when processing PreTestEvent «event.toString».''') - } else { - test.startTime = sysdate - } - run.status = '''«event.id»...''' - run.currentTestNumber = event.testNumber - run.currentTest = test - panel.update(reporterId) - } - - private def dispatch doProcess(PostTestEvent event) { - val test = run.getTest(event.id) - if (test === null) { - logger.severe('''Could not find test id "«event.id»"" when processing PostTestEvent «event.toString».''') - } else { - test.startTime = event.startTime - test.endTime = event.endTime - test.executionTime = event.executionTime - test.counter = event.counter - test.errorStack = event.errorStack - test.serverOutput = event.serverOutput - if (test.serverOutput !== null) { - run.infoCount = run.infoCount + 1 - } - test.failedExpectations = event.failedExpectations - test.warnings = event.warnings - if (test.warnings !== null) { - // it does not matter how many rows are used by utPLSQL to store a warning event - test.counter.warning = 1 - } else { - test.counter.warning = 0 - } - } - run.counter.disabled = run.counter.disabled + event.counter.disabled - run.counter.success = run.counter.success + event.counter.success - run.counter.failure = run.counter.failure + event.counter.failure - run.counter.error = run.counter.error + event.counter.error - run.counter.warning = run.counter.warning + test.counter.warning - panel.update(reporterId) - } - - private def void produce() { - try { - logger.fine('''Running utPLSQL tests and producing events via reporter id «reporterId»...''') - val dao = new RealtimeReporterDao(producerConn) - dao.produceReport(reporterId, pathList) - logger.fine('''All events produced for reporter id «reporterId».''') - } catch (Exception e) { - logger.severe('''Error while producing events for reporter id «reporterId»: «e?.message»''') - } - } - - private def void consume() { - try { - logger.fine('''Consuming events from reporter id «reporterId» in realtime...''') - val dao = new RealtimeReporterDao(consumerConn) - dao.consumeReport(reporterId, this) - logger.fine('''All events consumed.''') - } catch (Exception e) { - logger.severe('''Error while consuming events for reporter id «reporterId»: «e?.message»''') - } - if (run.totalNumberOfTests < 0) { - run.status = UtplsqlResources.getString("RUNNER_NO_TESTS_FOUND_TEXT") - run.executionTime = new Double(System.currentTimeMillis - run.start)/1000 - run.endTime = sysdate - run.totalNumberOfTests = 0 - panel.update(reporterId) - } - if (isRunningInSqlDeveloper) { - dispose - } - } - - private def isRunningInSqlDeveloper() { - return connectionName !== null - } - - private def initGUI() { - var RunnerView dockable = null - if (runningInSqlDeveloper && (dockable = RunnerFactory.dockable) === null) { - logger.severe('''Error getting utPLSQL dockable. Cannot run utPLSQL test.''') - return false - } else { - if (runningInSqlDeveloper) { - RunnerFactory.showDockable; - panel = dockable.runnerPanel - } else { - val frame = new JFrame("utPLSQL Runner Panel") - frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE; - panel = new RunnerPanel - frame.add(panel.getGUI) - frame.preferredSize = new Dimension(600, 800) - frame.pack - val dim = Toolkit.getDefaultToolkit().getScreenSize(); - frame.setLocation(dim.width / 2 - frame.getSize().width / 2, dim.height / 2 - frame.getSize().height / 2); - frame.setVisible(true) - } - initRun - } - return true - } - - def runTestAsync() { - // start tests when the GUI has been successfully initialized. - if (initGUI) { - // the consumer - val Runnable consumer = [|consume] - consumerThread = new Thread(consumer) - consumerThread.name = "realtime consumer" - consumerThread.start - // avoid concurrency on output header table to fix issue #80 - Thread.sleep(100) - // the producer - val Runnable producer = [|produce] - producerThread = new Thread(producer) - producerThread.name = "realtime producer" - producerThread.start - } - } - - def getProducerThread() { - return producerThread - } - - def getConsumerThread() { - return consumerThread - } - -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlWorksheetRunner.java b/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlWorksheetRunner.java new file mode 100644 index 00000000..b56b06fa --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlWorksheetRunner.java @@ -0,0 +1,153 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.runner; + +import java.awt.Container; +import java.util.List; +import java.util.logging.Logger; + +import javax.swing.JSplitPane; + +import org.utplsql.sqldev.exception.GenericDatabaseAccessException; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.StringTools; +import org.utplsql.sqldev.model.SystemTools; +import org.utplsql.sqldev.model.preference.PreferenceModel; +import org.utplsql.sqldev.resources.UtplsqlResources; + +import oracle.dbtools.worksheet.WorksheetResultPanel; +import oracle.dbtools.worksheet.editor.OpenWorksheetWizard; +import oracle.dbtools.worksheet.editor.Worksheet; +import oracle.dbtools.worksheet.utils.WorksheetUtil; +import oracle.ide.Ide; +import oracle.ide.config.Preferences; +import oracle.ide.controller.IdeAction; + +public class UtplsqlWorksheetRunner { + private static final Logger logger = Logger.getLogger(UtplsqlWorksheetRunner.class.getName()); + + private PreferenceModel preferences; + private String connectionName; + private List pathList; + + public UtplsqlWorksheetRunner(final List pathList, final String connectionName) { + this.pathList = pathList; + preferences = PreferenceModel.getInstance(Preferences.getPreferences()); + setConnection(connectionName); + } + + private void setConnection(final String connectionName) { + if (connectionName != null && preferences.isUnsharedWorksheet()) { + try { + this.connectionName = DatabaseTools.createTemporaryOrPrivateConnection(connectionName); + } catch (GenericDatabaseAccessException e) { + final String msg = "Failed to create temporary/private connection based on " + connectionName + "."; + logger.severe(() -> msg); + throw new GenericDatabaseAccessException(msg, e); + } + } else { + this.connectionName = connectionName; + } + } + + private CharSequence getCode() { + StringBuilder sb = new StringBuilder(); + if (preferences.isResetPackage()) { + sb.append("EXECUTE dbms_session.reset_package;\n"); + } + sb.append("SET SERVEROUTPUT ON SIZE UNLIMITED\n"); + if (preferences.isClearScreen()) { + sb.append("CLEAR SCREEN\n"); + } + final List paths = pathList; + if (paths.size() == 1) { + sb.append("EXECUTE ut.run('"); + sb.append(paths.get(0)); + sb.append("');\n"); + } else { + // we want a horizontal dense output because we resize the worksheet to fit the command in common cases + sb.append("EXECUTE ut.run(ut_varchar2_list("); + sb.append(StringTools.getCSV(pathList, "").replace("\n","")); + sb.append("));\n"); + } + return sb; + } + + private Worksheet openWorksheet() { + final Worksheet worksheet = (Worksheet) OpenWorksheetWizard.openNewTempWorksheet(connectionName, + getCode().toString()); + if (connectionName == null) { + worksheet.setComboConnection(null); + } + WorksheetUtil.setWorksheetTabName(worksheet.getContext().getNode().getURL(), + UtplsqlResources.getString("WORKSHEET_TITLE")); + worksheet.getContext().getNode().markDirty(false); + return worksheet; + } + + private void resizeResultPanel(final Worksheet worksheet) { + SystemTools.sleep(200); + Container splitPane = null; + WorksheetResultPanel selectedResultPanel = worksheet.getSelectedResultPanel(); + if (selectedResultPanel != null && selectedResultPanel.getGUI() != null && selectedResultPanel.getGUI().getParent() != null + && selectedResultPanel.getGUI().getParent().getParent() != null && selectedResultPanel.getGUI().getParent().getParent().getParent() != null) { + splitPane = selectedResultPanel.getGUI().getParent().getParent().getParent(); + } + if (splitPane instanceof JSplitPane) { + ((JSplitPane) splitPane).setDividerLocation(0.15); + } else { + final String msg = "Could not adjust size of worksheet. Expected JSplitPane but got " + + (splitPane != null ? splitPane.getClass().getName() : "null") + "."; + logger.severe(msg); + } + } + + private void runScript(final Worksheet worksheet) { + if (preferences.isAutoExecute()) { + SystemTools.sleep(100); + final IdeAction action = ((IdeAction) Ide.getIdeActionMap().get(Ide.findCmdID("Worksheet.RunScript"))); + if (action != null) { + try { + action.performAction(worksheet.getContext()); + } catch (Exception e) { + logger.severe(() -> "Could not run script in worksheet due to " + (e != null ? e.getMessage() : "???") + "."); + } + resizeResultPanel(worksheet); + } + } + } + + private void runTest() { + final Worksheet worksheet = openWorksheet(); + runScript(worksheet); + logger.fine(() -> "utPLSQL test called for " + pathList + " in " + connectionName + "."); + } + + public void runTestAsync() { + final Thread thread = new Thread(() -> runTest()); + thread.setName("utPLSQL run test"); + thread.start(); + } + + public static void openWithCode(final String code, final String connectionName) { + final Worksheet worksheet = (Worksheet) OpenWorksheetWizard.openNewTempWorksheet(connectionName, code); + if (connectionName == null) { + worksheet.setComboConnection(null); + } + WorksheetUtil.setWorksheetTabName(worksheet.getContext().getNode().getURL(), + UtplsqlResources.getString("WORKSHEET_TITLE")); + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlWorksheetRunner.xtend b/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlWorksheetRunner.xtend deleted file mode 100644 index bd03229a..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlWorksheetRunner.xtend +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.runner - -import java.util.List -import java.util.logging.Logger -import javax.swing.JSplitPane -import oracle.dbtools.raptor.utils.Connections -import oracle.dbtools.worksheet.editor.OpenWorksheetWizard -import oracle.dbtools.worksheet.editor.Worksheet -import oracle.dbtools.worksheet.utils.WorksheetUtil -import oracle.ide.Ide -import oracle.ide.config.Preferences -import oracle.ide.controller.IdeAction -import org.utplsql.sqldev.model.preference.PreferenceModel -import org.utplsql.sqldev.resources.UtplsqlResources - -class UtplsqlWorksheetRunner { - static val Logger logger = Logger.getLogger(UtplsqlWorksheetRunner.name); - - var PreferenceModel preferences - var String connectionName - var List pathList - - new(List pathList, String connectionName) { - this.pathList = pathList - this.preferences = PreferenceModel.getInstance(Preferences.preferences); - setConnection(connectionName) - } - - private def setConnection(String connectionName) { - if (connectionName !== null && preferences.unsharedWorksheet) { - // fix for issue #47 - private connections are not closed in SQLDev >= 17.4.0 - try { - // temporary connection is closed when worksheet is closed, but requires SQLDev >= 17.4.0 - this.connectionName = Connections.instance.createTemporaryConnection(connectionName) - } catch (Throwable e) { - // private connection is closed when worksheet is closed in SQLDev < 17.4.0 - this.connectionName = Connections.instance.createPrivateConnection(connectionName) - } - } else { - this.connectionName = connectionName; - } - } - - private def getCode() ''' - «IF preferences.resetPackage» - EXECUTE dbms_session.reset_package; - «ENDIF» - SET SERVEROUTPUT ON SIZE UNLIMITED - «IF preferences.clearScreen» - CLEAR SCREEN - «ENDIF» - «val paths = pathList» - «IF paths.size == 1» - EXECUTE ut.run('«paths.get(0)»'); - «ELSE» - EXECUTE ut.run(ut_varchar2_list(«FOR path : paths SEPARATOR ', '»'«path»'«ENDFOR»)); - «ENDIF» - ''' - - private def openWorksheet() { - val worksheet = OpenWorksheetWizard.openNewTempWorksheet(connectionName, code.toString) as Worksheet - if (connectionName === null) { - worksheet.comboConnection = null - } - WorksheetUtil.setWorksheetTabName(worksheet.context.node.URL, UtplsqlResources.getString("WORKSHEET_TITLE")) - worksheet.context.node.markDirty(false) - return worksheet - } - - private def resizeResultPanel(Worksheet worksheet) { - Thread.sleep(200) // give script runner time to initiate result panel - val splitPane = worksheet.selectedResultPanel?.GUI?.parent?.parent?.parent - if (splitPane instanceof JSplitPane) { - splitPane.dividerLocation = 0.15 // 15% for Worksheet, 85% for Script Output - } else { - logger. - severe('''Could not adjust size of worksheet. Expected JSplitPane but got «splitPane?.class?.name».''') - } - } - - private def runScript(Worksheet worksheet) { - if (preferences.autoExecute) { - Thread.sleep(100) // give worksheet time to initialize - val action = Ide.getIdeActionMap.get(Ide.findCmdID("Worksheet.RunScript")) as IdeAction - if (action !== null) { - action.performAction(worksheet.context) - worksheet.resizeResultPanel - } - } - } - - private def runTest() { - val worksheet = openWorksheet - worksheet.runScript - logger.fine('''utPLSQL test called for «pathList» in «connectionName».''') - } - - def runTestAsync() { - val Runnable runnable = [|runTest] - val thread = new Thread(runnable) - thread.name = "utPLSQL run test" - thread.start - } - - static def void openWithCode(String code, String connectionName) { - val worksheet = OpenWorksheetWizard.openNewTempWorksheet(connectionName, code) as Worksheet - if (connectionName === null) { - worksheet.comboConnection = null - } - WorksheetUtil.setWorksheetTabName(worksheet.context.node.URL, UtplsqlResources.getString("WORKSHEET_TITLE")) - } - -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/snippet/SnippetMerger.java b/sqldev/src/main/java/org/utplsql/sqldev/snippet/SnippetMerger.java new file mode 100644 index 00000000..ce940803 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/snippet/SnippetMerger.java @@ -0,0 +1,99 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.snippet; + +import java.io.BufferedReader; +import java.io.File; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.StringReader; +import java.nio.charset.Charset; +import java.nio.file.Paths; +import java.util.stream.Collectors; + +import javax.xml.parsers.DocumentBuilder; + +import org.utplsql.sqldev.model.FileTools; +import org.utplsql.sqldev.model.XMLTools; +import org.w3c.dom.Document; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; + +import oracle.dbtools.util.Resource; + +public class SnippetMerger { + private final XMLTools xmlTools = new XMLTools(); + private File userSnippetsFile; + private String utplsqlSnippets; + + public String getUtplsqlSnippetsAsString() { + final InputStream stream = getClass() + .getResourceAsStream("/org/utplsql/sqldev/resources/UtplsqlSnippets.xml"); + final BufferedReader reader = new BufferedReader(new InputStreamReader(stream, Charset.defaultCharset())); + return reader.lines().collect(Collectors.joining(System.lineSeparator())); + } + + public SnippetMerger() { + // works in SQL Developer only, otherwise a ExceptionInInitializerError is thrown + this(new File(Resource.RAPTOR_USER.getAbsolutePath() + File.separator + "UserSnippets.xml")); + } + + public SnippetMerger(final File file) { + utplsqlSnippets = getUtplsqlSnippetsAsString(); + userSnippetsFile = file; + } + + public void merge() { + String result = null; + if (userSnippetsFile.exists()) { + // file exists, proper merge required + final String userSnippets = new String(FileTools.readFile(Paths.get(userSnippetsFile.getAbsolutePath()))); + final DocumentBuilder docBuilder = xmlTools.createDocumentBuilder(); + final Document userSnippetsDoc = xmlTools.parse(docBuilder, new InputSource(new StringReader(userSnippets))); + final NodeList userSnippetsGroups = xmlTools.getNodeList(userSnippetsDoc, + "/snippets/group[not(@category=\"utPLSQL Annotations\" or @category=\"utPLSQL Expectations\")]"); + final Document utplsqlSnippetsDoc = xmlTools.parse(docBuilder, new InputSource(new StringReader(utplsqlSnippets))); + final NodeList utplsqlSnippetsGroups = xmlTools.getNodeList(utplsqlSnippetsDoc, "/snippets/group"); + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + sb.append("\n"); + for (int i = 0; i < userSnippetsGroups.getLength(); i++) { + sb.append(" "); + sb.append(xmlTools.nodeToString(userSnippetsGroups.item(i), "code")); + sb.append('\n'); + } + for (int i = 0; i < utplsqlSnippetsGroups.getLength(); i ++) { + sb.append(" "); + sb.append(xmlTools.nodeToString(utplsqlSnippetsGroups.item(i), "code")); + sb.append('\n'); + } + sb.append("\n"); + result = sb.toString(); + } else { + // just copy + result = utplsqlSnippets; + } + FileTools.writeFile(Paths.get(userSnippetsFile.getAbsolutePath()), result.getBytes()); + } + + public String getTemplate() { + return utplsqlSnippets; + } + + public File getFile() { + return userSnippetsFile; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/snippet/SnippetMerger.xtend b/sqldev/src/main/java/org/utplsql/sqldev/snippet/SnippetMerger.xtend deleted file mode 100644 index af3a7bb7..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/snippet/SnippetMerger.xtend +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.snippet - -import java.io.BufferedReader -import java.io.File -import java.io.IOException -import java.io.InputStreamReader -import java.io.StringReader -import java.nio.charset.Charset -import java.nio.file.Files -import java.nio.file.Paths -import java.util.stream.Collectors -import javax.xml.parsers.DocumentBuilderFactory -import oracle.dbtools.util.Resource -import org.utplsql.sqldev.model.XMLTools -import org.xml.sax.InputSource - -class SnippetMerger { - val extension XMLTools xmlTools = new XMLTools - File userSnippetsFile - String utplsqlSnippets - - def getUtplsqlSnippetsAsString() throws IOException { - val stream = class.getResourceAsStream("/org/utplsql/sqldev/resources/UtplsqlSnippets.xml") - val reader = new BufferedReader(new InputStreamReader(stream, Charset.defaultCharset)) - return reader.lines.collect(Collectors.joining(System.lineSeparator)) - } - - new() { - // works in SQL Developer only, otherwise a ExceptionInInitializerError is thrown - this (new File(Resource.RAPTOR_USER.absolutePath + File.separator + "UserSnippets.xml")) - } - - new(File file) { - utplsqlSnippets = utplsqlSnippetsAsString - userSnippetsFile = file - } - - def merge() { - var String result - if (userSnippetsFile.exists) { - // file exists, proper merge required - val userSnippets = new String(Files.readAllBytes(Paths.get(userSnippetsFile.absolutePath))) - val docBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder() - val userSnippetsDoc = docBuilder.parse(new InputSource(new StringReader(userSnippets))) - val userSnippetsGroups = userSnippetsDoc.getNodeList('''/snippets/group[not(@category="utPLSQL Annotations" or @category="utPLSQL Expectations")]''') - val utplsqlSnippetsDoc = docBuilder.parse(new InputSource(new StringReader(utplsqlSnippets))) - val utplsqlSnippetsGroups = utplsqlSnippetsDoc.getNodeList('''/snippets/group''') - result = ''' - - - «FOR i : 0 ..< userSnippetsGroups.length» - «userSnippetsGroups.item(i).nodeToString("code")» - «ENDFOR» - «FOR i : 0 ..< utplsqlSnippetsGroups.length» - «utplsqlSnippetsGroups.item(i).nodeToString("code")» - «ENDFOR» - - ''' - } else { - // just copy - result = utplsqlSnippets - - } - Files.write(Paths.get(userSnippetsFile.absolutePath), result.bytes) - } - - def getTemplate() { - return utplsqlSnippets - } - - def getFile() { - return userSnippetsFile - } - -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/common/DirectoryChooser.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/common/DirectoryChooser.java new file mode 100644 index 00000000..2d2fc8e9 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/common/DirectoryChooser.java @@ -0,0 +1,53 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.common; + +import java.io.File; +import java.util.logging.Logger; + +import javax.swing.JFileChooser; +import javax.swing.JFrame; +import javax.swing.JTextField; + +public class DirectoryChooser { + private static final Logger logger = Logger.getLogger(DirectoryChooser.class.getName()); + + // do not instantiate this class + private DirectoryChooser() { + super(); + } + + public static String choose(final JFrame parentFrame, final String title, final String initialDirectory) { + logger.finest(() -> "parantFrame: " + parentFrame); + String ret = null; + final JFileChooser chooser = new JFileChooser(); + chooser.setCurrentDirectory(new File(initialDirectory)); + chooser.setDialogTitle(title); + chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + chooser.setAcceptAllFileFilterUsed(false); + if (chooser.showOpenDialog(parentFrame) == JFileChooser.APPROVE_OPTION) { + ret = chooser.getSelectedFile().getAbsolutePath(); + } + return ret; + } + + public static void choose(final JFrame parentFrame, final String title, final JTextField textField) { + final String dir = choose(parentFrame, title, textField.getText()); + if (dir != null) { + textField.setText(dir); + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/common/DirectoryChooser.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/common/DirectoryChooser.xtend deleted file mode 100644 index adc057a2..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/common/DirectoryChooser.xtend +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.ui.common - -import java.io.File -import java.util.logging.Logger -import javax.swing.JFileChooser -import javax.swing.JFrame -import javax.swing.JTextField - -class DirectoryChooser { - val static Logger logger = Logger.getLogger(DirectoryChooser.name) - - def static choose (JFrame parentFrame, String title, String initialDirectory) { - logger.finest('''parantFrame: «parentFrame»''') - var String ret = null - val chooser = new JFileChooser() - chooser.currentDirectory = new File(initialDirectory) - chooser.dialogTitle = title - chooser.fileSelectionMode = JFileChooser.DIRECTORIES_ONLY - chooser.acceptAllFileFilterUsed = false - if (chooser.showOpenDialog(parentFrame) == JFileChooser.APPROVE_OPTION) { - ret = chooser.selectedFile.absolutePath - } - return ret - } - - def static void choose (JFrame parentFrame, String title, JTextField textField) { - val dir = choose(parentFrame, title, textField.text) - if (dir !== null) { - textField.text = dir - } - } - -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/coverage/CodeCoverageReporterDialog.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/coverage/CodeCoverageReporterDialog.java new file mode 100644 index 00000000..029c8264 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/coverage/CodeCoverageReporterDialog.java @@ -0,0 +1,223 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.coverage; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.Toolkit; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; +import java.awt.event.WindowEvent; + +import javax.swing.BorderFactory; +import javax.swing.JButton; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JScrollPane; +import javax.swing.JTextArea; +import javax.swing.JTextField; +import javax.swing.ScrollPaneConstants; +import javax.swing.SwingUtilities; + +import org.utplsql.sqldev.coverage.CodeCoverageReporter; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.StringTools; +import org.utplsql.sqldev.resources.UtplsqlResources; + +public class CodeCoverageReporterDialog extends JFrame implements ActionListener, FocusListener { + private static final long serialVersionUID = 5503685225300993401L; + + private CodeCoverageReporter reporter; + private JButton runButton; + private JButton cancelButton; + private JPanel paneParams; + private int paramPos = (-1); + private final JTextArea pathsTextArea = new JTextArea(); + private final JTextField schemasTextField = new JTextField(); + private final JTextArea includeObjectsTextArea = new JTextArea(); + private final JTextArea excludeObjectsTextArea = new JTextArea(); + + public static void createAndShow(final CodeCoverageReporter reporter) { + SwingUtilities.invokeLater(() -> CodeCoverageReporterDialog.createAndShowWithinEventThread(reporter)); + } + + private static void createAndShowWithinEventThread(final CodeCoverageReporter reporter) { + // create and layout the dialog + final CodeCoverageReporterDialog frame = new CodeCoverageReporterDialog(reporter); + reporter.setFrame(frame); + frame.pack(); + // center dialog + final Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); + frame.setLocation(dim.width / 2 - frame.getSize().width / 2, dim.height / 2 - frame.getSize().height / 2); + frame.setAlwaysOnTop(true); + frame.setVisible(true); + } + + public CodeCoverageReporterDialog(final CodeCoverageReporter reporter) { + super(UtplsqlResources.getString("WINDOW_CODE_COVERAGE_REPORT_LABEL")); + this.reporter = reporter; + final Container pane = getContentPane(); + pane.setLayout(new GridBagLayout()); + final GridBagConstraints c = new GridBagConstraints(); + + // parameters pane + paneParams = new JPanel(new GridBagLayout()); + pathsTextArea.setEditable(false); + pathsTextArea.setEnabled(false); + addParam(UtplsqlResources.getString("WINDOW_PATHS_LABEL"), StringTools.getSimpleCSV(reporter.getPathList()), pathsTextArea, 50, 2); + addParam(UtplsqlResources.getString("WINDOW_SCHEMAS_LABEL"), "", schemasTextField, 0, 0); + addParam(UtplsqlResources.getString("WINDOW_INCLUDE_OBJECS_LABEL"), StringTools.getSimpleCSV(reporter.getIncludeObjectList()), includeObjectsTextArea, 66, 4); + addParam(UtplsqlResources.getString("WINDOW_EXCLUDE_OBJECS_LABEL"), "", excludeObjectsTextArea, 34, 1); + final JScrollPane scrollPane = new JScrollPane(paneParams); + scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); + scrollPane.setHorizontalScrollBarPolicy(ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER); + scrollPane.setBorder(BorderFactory.createEmptyBorder()); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + c.insets = new Insets(10, 10, 0, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.NORTH; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + pane.add(scrollPane, c); + + // Buttons pane + final JPanel panelButtons = new JPanel(new GridBagLayout()); + runButton = new JButton(UtplsqlResources.getString("WINDOW_RUN_BUTTON")); + runButton.addActionListener(this); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.insets = new Insets(0, 0, 0, 0); // top, left, bottom, right + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + c.weighty = 0; + panelButtons.add(runButton, c); + cancelButton = new JButton(UtplsqlResources.getString("WINDOW_CANCEL_BUTTON")); + cancelButton.addActionListener(this); + c.gridx = 1; + c.insets = new Insets(0, 10, 0, 0); // top, left, bottom, right + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + c.weighty = 0; + panelButtons.add(cancelButton, c); + c.gridx = 1; + c.gridy = 1; + c.gridwidth = 1; + c.insets = new Insets(30, 10, 10, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.EAST; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + c.weighty = 0; + pane.add(panelButtons, c); + pane.setPreferredSize(new Dimension(500, 320)); + SwingUtilities.getRootPane(runButton).setDefaultButton(runButton); + } + + private void addParam(final String label, final String text, final Component component, final int height, + final double weighty) { + paramPos++; + final GridBagConstraints c = new GridBagConstraints(); + final JLabel paramLabel = new JLabel(label); + c.gridx = 0; + c.gridy = paramPos; + c.gridwidth = 1; + c.insets = new Insets(10, 10, 0, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.NORTHWEST; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 0; + c.weighty = 0; + paneParams.add(paramLabel, c); + c.gridx = 1; + c.gridwidth = GridBagConstraints.REMAINDER; + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.insets = new Insets(10, 10, 0, 10); // top, left, bottom, right + c.weightx = 1; + c.weighty = weighty; + if (component instanceof JTextField) { + ((JTextField) component).setText(text); + paneParams.add(component, c); + } else if (component instanceof JTextArea) { + ((JTextArea) component).setText(text); + ((JTextArea) component).setLineWrap(true); + ((JTextArea) component).setWrapStyleWord(true); + JScrollPane scrollPane = new JScrollPane(component); + scrollPane.getViewport().setPreferredSize(new Dimension(200, height)); + paneParams.add(scrollPane, c); + } + component.addFocusListener(this); + } + + public void exit() { + dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING)); + } + + @Override + public void actionPerformed(final ActionEvent e) { + if (e.getSource() == runButton) { + reporter.setSchemas(schemasTextField.getText()); + reporter.setIncludeObjects(includeObjectsTextArea.getText()); + reporter.setExcludeObjects(excludeObjectsTextArea.getText()); + schemasTextField.setEnabled(false); + includeObjectsTextArea.setEnabled(false); + excludeObjectsTextArea.setEnabled(false); + runButton.setEnabled(false); + reporter.runAsync(); + } else { + if (e.getSource() == cancelButton) { + if (runButton.isEnabled()) { + // report is not yet started, just close the window + exit(); + } else { + // report is being created... + // frame will close as soon as the connection is technically aborted + // database session is not cancelled. This is not a bug. + // to cancel the session you have to kill it via "ALTER SYSTEM KILL SESSION". + // However, the abort frees all resources on the client side. + DatabaseTools.abortConnection(reporter.getConnection()); + } + } + } + } + + @Override + public void focusGained(final FocusEvent e) { + if (paneParams.isAncestorOf(e.getComponent())) { + // make component at cursor position is visible + final int x = e.getComponent().getLocationOnScreen().x - paneParams.getLocationOnScreen().x; + final int y = e.getComponent().getLocationOnScreen().y - paneParams.getLocationOnScreen().y; + final int width = e.getComponent().getBounds().width; + final int height = e.getComponent().getBounds().height; + final Rectangle rect = new Rectangle(x, y, width, height); + paneParams.scrollRectToVisible(rect); + } + } + + @Override + public void focusLost(final FocusEvent e) { + // ignore + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/coverage/CodeCoverageReporterDialog.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/coverage/CodeCoverageReporterDialog.xtend deleted file mode 100644 index 1c57eb6e..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/coverage/CodeCoverageReporterDialog.xtend +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.ui.coverage - -import java.awt.Component -import java.awt.Dimension -import java.awt.GridBagConstraints -import java.awt.GridBagLayout -import java.awt.Insets -import java.awt.Rectangle -import java.awt.Toolkit -import java.awt.event.ActionEvent -import java.awt.event.ActionListener -import java.awt.event.FocusEvent -import java.awt.event.FocusListener -import java.awt.event.WindowEvent -import javax.swing.BorderFactory -import javax.swing.JButton -import javax.swing.JFrame -import javax.swing.JLabel -import javax.swing.JPanel -import javax.swing.JScrollPane -import javax.swing.JTextArea -import javax.swing.JTextField -import javax.swing.ScrollPaneConstants -import javax.swing.SwingUtilities -import org.springframework.core.task.SimpleAsyncTaskExecutor -import org.utplsql.sqldev.coverage.CodeCoverageReporter -import org.utplsql.sqldev.resources.UtplsqlResources - -class CodeCoverageReporterDialog extends JFrame implements ActionListener, FocusListener { - - var CodeCoverageReporter reporter - var JButton runButton - var JButton cancelButton - var JPanel paneParams; - var int paramPos = -1; - val pathsTextArea = new JTextArea() - val schemasTextField = new JTextField() - val includeObjectsTextArea = new JTextArea() - val excludeObjectsTextArea = new JTextArea() - - def static createAndShow(CodeCoverageReporter reporter) { - SwingUtilities.invokeLater(new Runnable() { - override run() { - CodeCoverageReporterDialog.createAndShowWithinEventThread(reporter); - } - }); - } - - private def static createAndShowWithinEventThread(CodeCoverageReporter reporter) { - // create and layout the dialog - val frame = new CodeCoverageReporterDialog(reporter) - reporter.frame = frame - frame.pack - // center dialog - val dim = Toolkit.getDefaultToolkit().getScreenSize(); - frame.setLocation(dim.width / 2 - frame.getSize().width / 2, dim.height / 2 - frame.getSize().height / 2); - frame.alwaysOnTop = true - frame.visible = true - } - - new(CodeCoverageReporter reporter) { - super(UtplsqlResources.getString("WINDOW_CODE_COVERAGE_REPORT_LABEL")) - this.reporter = reporter - val pane = getContentPane(); - pane.setLayout(new GridBagLayout()); - val c = new GridBagConstraints(); - - // parameters pane - paneParams = new JPanel(new GridBagLayout()) - pathsTextArea.editable = false - pathsTextArea.enabled = false - addParam(UtplsqlResources.getString("WINDOW_PATHS_LABEL"), '''«FOR path : reporter.pathList SEPARATOR ", "»«path»«ENDFOR»''', pathsTextArea, 50, 2) - addParam(UtplsqlResources.getString("WINDOW_SCHEMAS_LABEL"), "", schemasTextField, 0, 0); - addParam(UtplsqlResources.getString("WINDOW_INCLUDE_OBJECS_LABEL"), '''«FOR i : reporter.includeObjectList SEPARATOR ", "»«i»«ENDFOR»''', includeObjectsTextArea, 66, 4); - addParam(UtplsqlResources.getString("WINDOW_EXCLUDE_OBJECS_LABEL"), "", excludeObjectsTextArea, 34, 1); - val scrollPane = new JScrollPane(paneParams) - scrollPane.verticalScrollBarPolicy = ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED - scrollPane.horizontalScrollBarPolicy = ScrollPaneConstants.HORIZONTAL_SCROLLBAR_NEVER - scrollPane.border = BorderFactory.createEmptyBorder; - c.gridx = 0; - c.gridy = 0; - c.gridwidth = 2; - c.insets = new Insets(10, 10, 0, 10); // top, left, bottom, right - c.anchor = GridBagConstraints.NORTH; - c.fill = GridBagConstraints.BOTH; - c.weightx = 1; - c.weighty = 1; - pane.add(scrollPane, c) - - // Buttons pane - val panelButtons = new JPanel(new GridBagLayout()) - runButton = new JButton(UtplsqlResources.getString("WINDOW_RUN_BUTTON")) - runButton.addActionListener(this); - c.gridx = 0; - c.gridy = 0; - c.gridwidth = 1; - c.insets = new Insets(0, 0, 0, 0); // top, left, bottom, right - c.fill = GridBagConstraints.NONE; - c.weightx = 0; - c.weighty = 0; - panelButtons.add(runButton, c); - cancelButton = new JButton(UtplsqlResources.getString("WINDOW_CANCEL_BUTTON")); - cancelButton.addActionListener(this); - c.gridx = 1; - c.insets = new Insets(0, 10, 0, 0); // top, left, bottom, right - c.fill = GridBagConstraints.NONE; - c.weightx = 0; - c.weighty = 0; - panelButtons.add(cancelButton, c); - c.gridx = 1; - c.gridy = 1; - c.gridwidth = 1; - c.insets = new Insets(30, 10, 10, 10); // top, left, bottom, right - c.anchor = GridBagConstraints.EAST - c.fill = GridBagConstraints.NONE - c.weightx = 0; - c.weighty = 0; - pane.add(panelButtons, c); - pane.setPreferredSize(new Dimension(500, 320)); - SwingUtilities.getRootPane(runButton).defaultButton = runButton - } - - private def addParam(String label, String text, Component component, int height, double weighty) { - paramPos++ - val c = new GridBagConstraints(); - val paramLabel = new JLabel(label) - c.gridx = 0 - c.gridy = paramPos - c.gridwidth = 1 - c.insets = new Insets(10, 10, 0, 0) // top, left, bottom, right - c.anchor = GridBagConstraints.NORTHWEST - c.fill = GridBagConstraints.HORIZONTAL - c.weightx = 0 - c.weighty = 0 - paneParams.add(paramLabel, c); - c.gridx = 1 - c.gridwidth = GridBagConstraints.REMAINDER - c.anchor = GridBagConstraints.WEST - c.fill = GridBagConstraints.BOTH - c.insets = new Insets(10, 10, 0, 10); // top, left, bottom, right - c.weightx = 1 - c.weighty = weighty - if (component instanceof JTextField) { - component.text = text - paneParams.add(component, c) - } else if (component instanceof JTextArea) { - component.text = text - component.lineWrap = true - component.wrapStyleWord = true - var scrollPane = new JScrollPane(component); - scrollPane.viewport.preferredSize = new Dimension(200, height) - paneParams.add(scrollPane, c) - } - component.addFocusListener(this) - } - - def exit() { - dispatchEvent(new WindowEvent(this, WindowEvent.WINDOW_CLOSING)); - } - - override actionPerformed(ActionEvent e) { - if (e.getSource == runButton) { - reporter.schemas = schemasTextField.text - reporter.includeObjects = includeObjectsTextArea.text - reporter.excludeObjects = excludeObjectsTextArea.text - schemasTextField.setEnabled(false) - includeObjectsTextArea.setEnabled(false) - excludeObjectsTextArea.setEnabled(false) - runButton.setEnabled(false) - reporter.runAsync - } else if (e.getSource == cancelButton) { - if (runButton.enabled) { - // report is not yet started, just close the window - exit - } else { - // report is being created... - // frame will close as soon as the connection is technically aborted - // database session is not cancelled. This is not a bug. - // to cancel the session you have to kill it via "ALTER SYSTEM KILL SESSION". - // However, the abort frees all resources on the client side. - reporter.connection.abort(new SimpleAsyncTaskExecutor) - } - } - } - - override focusGained(FocusEvent e) { - if (paneParams.isAncestorOf(e.component)) { - // make component at cursor position is visible - val x = e.component.getLocationOnScreen.x - paneParams.getLocationOnScreen.x - val y = e.component.getLocationOnScreen.y - paneParams.getLocationOnScreen.y - val width = e.component.getBounds.width - val height = e.component.getBounds.height - val rect = new Rectangle(x, y, width, height) - paneParams.scrollRectToVisible(rect) - } - } - - override focusLost(FocusEvent e) { - // ignore - } - -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/preference/PreferencePanel.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/preference/PreferencePanel.java new file mode 100644 index 00000000..89786dae --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/preference/PreferencePanel.java @@ -0,0 +1,438 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.preference; + +import java.util.Map; + +import javax.swing.JButton; +import javax.swing.JCheckBox; +import javax.swing.JOptionPane; +import javax.swing.JPanel; +import javax.swing.JSpinner; +import javax.swing.JTabbedPane; +import javax.swing.JTextField; +import javax.swing.SpinnerNumberModel; +import javax.swing.table.DefaultTableModel; + +import org.utplsql.sqldev.model.StringTools; +import org.utplsql.sqldev.model.preference.PreferenceModel; +import org.utplsql.sqldev.resources.UtplsqlResources; +import org.utplsql.sqldev.snippet.SnippetMerger; +import org.utplsql.sqldev.ui.common.DirectoryChooser; + +import oracle.dbtools.raptor.templates.CodeTemplateUtil; +import oracle.ide.panels.DefaultTraversablePanel; +import oracle.ide.panels.TraversableContext; +import oracle.ide.panels.TraversalException; +import oracle.javatools.ui.layout.FieldLayoutBuilder; + +public class PreferencePanel extends DefaultTraversablePanel { + private static final long serialVersionUID = -2583957375062007813L; + private final JPanel runTestPanel = new JPanel(); + private final JCheckBox useRealtimeReporterCheckBox = new JCheckBox(); + private final JCheckBox unsharedWorksheetCheckBox = new JCheckBox(); + private final JCheckBox resetPackageCheckBox = new JCheckBox(); + private final JCheckBox clearScreenCheckBox = new JCheckBox(); + private final JCheckBox autoExecuteCheckBox = new JCheckBox(); + private final JCheckBox checkRunUtplsqlTestCheckBox = new JCheckBox(); + private final JCheckBox useSmartTimesCheckBox = new JCheckBox(); + private final JButton importSnippetsButton = new JButton( + UtplsqlResources.getString("PREF_IMPORT_SNIPPETS_BUTTON_LABEL")); + private final JPanel realtimeReporterPanel = new JPanel(); + private final SpinnerNumberModel numberOfRunsInHistoryModel = new SpinnerNumberModel(1, 1, 100, 1); + private final JSpinner numberOfRunsInHistorySpinner = new JSpinner(numberOfRunsInHistoryModel); + private final JCheckBox showDisabledCounterCheckBox = new JCheckBox(); + private final JCheckBox showWarningsCounterCheckBox = new JCheckBox(); + private final JCheckBox showInfoCounterCheckBox = new JCheckBox(); + private final JCheckBox showWarningIndicatorCheckBox = new JCheckBox(); + private final JCheckBox showInfoIndicatorCheckBox = new JCheckBox(); + private final JCheckBox showSuccessfulTestsCheckBox = new JCheckBox(); + private final JCheckBox showDisabledTestsCheckBox = new JCheckBox(); + private final JCheckBox showTestDescriptionCheckBox = new JCheckBox(); + private final JCheckBox syncDetailTabCheckBox = new JCheckBox(); + private final JPanel generateTestPanel = new JPanel(); + private final JTextField testPackagePrefixTextField = new JTextField(); + private final JTextField testPackageSuffixTextField = new JTextField(); + private final JTextField testUnitPrefixTextField = new JTextField(); + private final JTextField testUnitSuffixTextField = new JTextField(); + private final SpinnerNumberModel numberOfTestsPerUnitModel = new SpinnerNumberModel(1, 1, 10, 1); + private final JSpinner numberOfTestsPerUnitSpinner = new JSpinner(numberOfTestsPerUnitModel); + private final JCheckBox checkGenerateUtplsqlTestCheckBox = new JCheckBox(); + private final DefaultTableModel codeTemplatesModel = new DefaultTableModel(new Object[] { "Id", "Template" }, 0); + private final JButton createCodeTemplatesButton = new JButton(); + private final JCheckBox generateCommentsCheckBox = new JCheckBox(); + private final JCheckBox disableTestsCheckBox = new JCheckBox(); + private final JTextField suitePathTextField = new JTextField(); + private final SpinnerNumberModel indentSpacesModel = new SpinnerNumberModel(1, 1, 8, 1); + private final JSpinner indentSpacesSpinner = new JSpinner(indentSpacesModel); + private final JPanel oddgenPanel = new JPanel(); + private final JTextField rootFolderInOddgenViewTextField = new JTextField(); + private final JCheckBox generateFilesCheckBox = new JCheckBox(); + private final JTextField outputDirectoryTextField = new JTextField(); + private final JButton outputDirectoryBrowse = new JButton(); + private final JCheckBox deleteExistingFilesCheckBox = new JCheckBox(); + + public PreferencePanel() { + layoutControls(); + } + + private void layoutControls() { + // run test group + final FieldLayoutBuilder runTab = new FieldLayoutBuilder(runTestPanel); + runTab.setAlignLabelsLeft(true); + runTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_USE_REALTIME_REPORTER_LABEL")) + .component(useRealtimeReporterCheckBox) + .withHint(UtplsqlResources.getString("PREF_USE_REALTIME_REPORTER_HINT"))); + runTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_UNSHARED_WORKSHEET_LABEL")) + .component(unsharedWorksheetCheckBox)); + runTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_RESET_PACKAGE_LABEL")) + .component(resetPackageCheckBox)); + runTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_CLEAR_SCREEN_LABEL")) + .component(clearScreenCheckBox)); + runTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_AUTO_EXECUTE_LABEL")) + .component(autoExecuteCheckBox)); + runTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_CHECK_RUN_UTPLSQL_TEST_LABEL")) + .component(checkRunUtplsqlTestCheckBox)); + runTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_USE_SMART_TIMES_LABEL")) + .component(useSmartTimesCheckBox)); + runTab.addVerticalGap(); + runTab.addRow(importSnippetsButton); + runTab.addVerticalSpring(); + + // realtime reporter group + final FieldLayoutBuilder rrTab = new FieldLayoutBuilder(realtimeReporterPanel); + rrTab.setAlignLabelsLeft(true); + rrTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_NUMBER_OF_RUNS_IN_HISTORY_LABEL")) + .component(numberOfRunsInHistorySpinner)); + rrTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_SHOW_DISABLED_COUNTER_LABEL")) + .component(showDisabledCounterCheckBox)); + rrTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_SHOW_WARNINGS_COUNTER_LABEL")) + .component(showWarningsCounterCheckBox)); + rrTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_SHOW_INFO_COUNTER_LABEL")) + .component(showInfoCounterCheckBox)); + rrTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_SHOW_WARNING_INDICATOR_LABEL")) + .component(showWarningIndicatorCheckBox)); + rrTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_SHOW_INFO_INDICATOR_LABEL")) + .component(showInfoIndicatorCheckBox)); + rrTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_SHOW_SUCCESSFUL_TESTS_LABEL")) + .component(showSuccessfulTestsCheckBox)); + rrTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_SHOW_DISABLED_TESTS_LABEL")) + .component(showDisabledTestsCheckBox)); + rrTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_SHOW_TEST_DESCRIPTION_LABEL")) + .component(showTestDescriptionCheckBox)); + rrTab.add(runTab.field().label().withText(UtplsqlResources.getString("PREF_SYNC_DETAIL_TAB_LABEL")) + .component(syncDetailTabCheckBox)); + rrTab.addVerticalSpring(); + + // generate test group + final FieldLayoutBuilder generateTab = new FieldLayoutBuilder(generateTestPanel); + generateTab.setAlignLabelsLeft(true); + generateTab.setStretchComponentsWithNoButton(true); + generateTab + .add(generateTab.field().label().withText(UtplsqlResources.getString("PREF_TEST_PACKAGE_PREFIX_LABEL")) + .component(testPackagePrefixTextField)); + generateTab + .add(generateTab.field().label().withText(UtplsqlResources.getString("PREF_TEST_PACKAGE_SUFFIX_LABEL")) + .component(testPackageSuffixTextField)); + generateTab.add(generateTab.field().label().withText(UtplsqlResources.getString("PREF_TEST_UNIT_PREFIX_LABEL")) + .component(testUnitPrefixTextField)); + generateTab.add(generateTab.field().label().withText(UtplsqlResources.getString("PREF_TEST_UNIT_SUFFIX_LABEL")) + .component(testUnitSuffixTextField)); + generateTab.add( + generateTab.field().label().withText(UtplsqlResources.getString("PREF_NUMBER_OF_TESTS_PER_UNIT_LABEL")) + .component(numberOfTestsPerUnitSpinner)); + generateTab.add(generateTab.field().label().withText(UtplsqlResources.getString("PREF_GENERATE_COMMENTS_LABEL")) + .component(generateCommentsCheckBox)); + generateTab.add(generateTab.field().label().withText(UtplsqlResources.getString("PREF_DISABLE_TESTS_LABEL")) + .component(disableTestsCheckBox)); + generateTab.add(generateTab.field().label().withText(UtplsqlResources.getString("PREF_SUITE_PATH_LABEL")) + .component(suitePathTextField)); + generateTab.add(generateTab.field().label().withText(UtplsqlResources.getString("PREF_INDENT_SPACES_LABEL")) + .component(indentSpacesSpinner)); + generateTab.add(generateTab.field().label() + .withText(UtplsqlResources.getString("PREF_CHECK_GENERATE_UTPLSQL_TEST_LABEL")) + .component(checkGenerateUtplsqlTestCheckBox).button(createCodeTemplatesButton) + .withText(UtplsqlResources.getString("PREF_CREATE_CODE_TEMPLATES_BUTTON_LABEL"))); + generateTab.addVerticalSpring(); + + // oddgen group + final FieldLayoutBuilder oddgenTab = new FieldLayoutBuilder(oddgenPanel); + oddgenTab.setAlignLabelsLeft(true); + oddgenTab.setStretchComponentsWithNoButton(true); + oddgenTab.add( + oddgenTab.field().label().withText(UtplsqlResources.getString("PREF_ROOT_FOLDER_IN_ODDGEN_VIEW_LABEL")) + .component(rootFolderInOddgenViewTextField)); + oddgenTab.add(oddgenTab.field().label().withText(UtplsqlResources.getString("PREF_GENERATE_FILES_LABEL")) + .component(generateFilesCheckBox)); + oddgenTab.add(oddgenTab.field().label().withText(UtplsqlResources.getString("PREF_OUTPUT_DIRECTORY_LABEL")) + .component(outputDirectoryTextField).button(outputDirectoryBrowse) + .withText(UtplsqlResources.getString("PREF_OUTPUT_DIRECTORY_BUTTON_LABEL"))); + oddgenTab.add(oddgenTab.field().label().withText(UtplsqlResources.getString("PREF_DELETE_EXISTING_FILES_LABEL")) + .component(deleteExistingFilesCheckBox)); + oddgenTab.addVerticalSpring(); + + // putting groups into tabbed panes + final JTabbedPane tabbedPane = new JTabbedPane(); + tabbedPane.add(UtplsqlResources.getString("MENU_RUN_TEST_LABEL"), runTestPanel); + tabbedPane.add(UtplsqlResources.getString("MENU_REALTIME_REPORTER_LABEL"), realtimeReporterPanel); + tabbedPane.add(UtplsqlResources.getString("MENU_GENERATE_TEST_LABEL"), generateTestPanel); + tabbedPane.add("oddgen", oddgenPanel); + final FieldLayoutBuilder builder = new FieldLayoutBuilder(this); + builder.setAlignLabelsLeft(true); + builder.addVerticalField("", tabbedPane); + builder.addVerticalSpring(); + + // register action listener for import snippets button + importSnippetsButton.addActionListener(event -> importSnippets()); + + // register action listener for create code template button + createCodeTemplatesButton.addActionListener(event -> saveCodeTemplates()); + + // register action listener for directory chooser + outputDirectoryBrowse.addActionListener(event -> DirectoryChooser.choose(null, + UtplsqlResources.getString("PREF_OUTPUT_DIRECTORY_LABEL"), outputDirectoryTextField)); + } + + private void importSnippets() { + final SnippetMerger snippetMerger = new SnippetMerger(); + snippetMerger.merge(); + final String file = snippetMerger.getFile().getAbsolutePath(); + final String message = String.format(UtplsqlResources.getString("PREF_CONFIRM_IMPORT_MESSAGE"), file); + JOptionPane.showMessageDialog(null, message, UtplsqlResources.getString("PREF_CONFIRM_IMPORT_TITLE"), + JOptionPane.INFORMATION_MESSAGE); + } + + private void loadCodeTemplates() { + final Map map = CodeTemplateUtil.loadFiles(); + for (final Map.Entry entry : map.entrySet()) { + codeTemplatesModel.addRow(new Object[] { entry.getKey(), entry.getValue() }); + } + } + + private void saveCodeTemplates() { + Integer indentSpaces = (Integer) indentSpacesSpinner.getValue(); + codeTemplatesModel + .addRow(new Object[] { "ut_spec", StringTools.replaceTabsWithSpaces(utSpecTemplate(), indentSpaces) }); + codeTemplatesModel.addRow(new Object[] { "ut_spec_proc", + trimPlusNewLine(StringTools.replaceTabsWithSpaces(utSpecProcTemplate(), indentSpaces)) }); + codeTemplatesModel + .addRow(new Object[] { "ut_body", StringTools.replaceTabsWithSpaces(utBodyTemplate(), indentSpaces) }); + codeTemplatesModel.addRow(new Object[] { "ut_body_proc", + trimPlusNewLine(StringTools.replaceTabsWithSpaces(utBodyProcTemplate(), indentSpaces)) }); + CodeTemplateUtil.save(codeTemplatesModel); + } + + private String trimPlusNewLine(final String input) { + return input.trim() + System.lineSeparator(); + } + + private CharSequence utSpecTemplate() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE "); + sb.append(testPackagePrefixTextField.getText()); + sb.append("[package_name]"); + sb.append(testPackageSuffixTextField.getText()); + sb.append(" IS\n\n"); + sb.append("\t--%suite\n"); + if (!suitePathTextField.getText().isEmpty()) { + sb.append("\t--%suitepath("); + sb.append(suitePathTextField.getText()); + sb.append(")\n"); + } + sb.append('\n'); + sb.append('\t'); + sb.append(utSpecProcTemplate()); + sb.append("END "); + sb.append(testPackagePrefixTextField.getText()); + sb.append("[package_name]"); + sb.append(testPackageSuffixTextField.getText()); + sb.append(";\n"); + sb.append("/\n"); + return sb; + } + + private CharSequence utSpecProcTemplate() { + StringBuilder sb = new StringBuilder(); + final Integer numberOfTestsPerUnit = (Integer) numberOfTestsPerUnitModel.getValue(); + final boolean withContext = numberOfTestsPerUnit > 1; + if (withContext) { + sb.append("--%context([procedure_name])\n\n"); + } + for (int i = 0; i < numberOfTestsPerUnit; i ++) { + sb.append("--%test\n"); + if (disableTestsCheckBox.isSelected()) { + sb.append("--%disabled\n"); + } + sb.append("PROCEDURE "); + sb.append(testUnitPrefixTextField.getText()); + sb.append("[procedure_name]"); + sb.append(testUnitSuffixTextField.getText()); + if (withContext) { + sb.append(i); + } + sb.append(";\n\n"); + } + if (withContext) { + sb.append("--%endcontext\n\n"); + } + return sb; + } + + private CharSequence utBodyTemplate() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE BODY "); + sb.append(testPackagePrefixTextField.getText()); + sb.append("[package_name]"); + sb.append(testPackageSuffixTextField.getText()); + sb.append(" IS\n\n"); + sb.append('\t'); + sb.append(utBodyProcTemplate()); + sb.append('\n'); + sb.append("END "); + sb.append(testPackagePrefixTextField.getText()); + sb.append("[package_name]"); + sb.append(testPackageSuffixTextField.getText()); + sb.append(";\n"); + sb.append("/\n"); + return sb; + } + + private CharSequence utBodyProcTemplate() { + StringBuilder sb = new StringBuilder(); + final Integer numberOfTestsPerUnit = (Integer) numberOfTestsPerUnitModel.getValue(); + final boolean withContext = numberOfTestsPerUnit > 1; + for (int i = 0; i < numberOfTestsPerUnit; i++) { + if (generateCommentsCheckBox.isSelected()) { + sb.append("--\n"); + sb.append("-- test"); + if (withContext) { + sb.append(" [procedure_name] case "); + sb.append(i); + sb.append(": ..."); + } + sb.append('\n'); + sb.append("--\n"); + } + sb.append("PROCEDURE "); + sb.append(testUnitPrefixTextField.getText()); + sb.append("[procedure_name]"); + sb.append(testUnitSuffixTextField.getText()); + if (withContext) { + sb.append(i); + } + sb.append(" IS\n"); + sb.append("\tl_actual INTEGER := 0;\n"); + sb.append("\tl_expected INTEGER := 1;\n"); + sb.append("BEGIN\n"); + if (generateCommentsCheckBox.isSelected()) { + sb.append("\t-- populate actual\n"); + sb.append("\t-- ...\n\n"); + sb.append("\t-- populate expected\n"); + sb.append("\t-- ...\n\n"); + sb.append("\t-- assert\n"); + } + sb.append("\tut.expect(l_actual).to_equal(l_expected);"); + sb.append("END "); + sb.append(testUnitPrefixTextField.getText()); + sb.append("[procedure_name]"); + sb.append(testUnitSuffixTextField.getText()); + if (withContext) { + sb.append(i); + } + sb.append(";\n\n"); + } + return sb; + } + + @Override + public void onEntry(final TraversableContext traversableContext) { + PreferenceModel info = getUserInformation(traversableContext); + useRealtimeReporterCheckBox.setSelected(info.isUseRealtimeReporter()); + unsharedWorksheetCheckBox.setSelected(info.isUnsharedWorksheet()); + resetPackageCheckBox.setSelected(info.isResetPackage()); + clearScreenCheckBox.setSelected(info.isClearScreen()); + autoExecuteCheckBox.setSelected(info.isAutoExecute()); + checkRunUtplsqlTestCheckBox.setSelected(info.isCheckRunUtplsqlTest()); + useSmartTimesCheckBox.setSelected(info.isUseSmartTimes()); + numberOfRunsInHistorySpinner.setValue(info.getNumberOfRunsInHistory()); + showDisabledCounterCheckBox.setSelected(info.isShowDisabledCounter()); + showWarningsCounterCheckBox.setSelected(info.isShowWarningsCounter()); + showInfoCounterCheckBox.setSelected(info.isShowInfoCounter()); + showWarningIndicatorCheckBox.setSelected(info.isShowWarningIndicator()); + showInfoIndicatorCheckBox.setSelected(info.isShowInfoIndicator()); + showSuccessfulTestsCheckBox.setSelected(info.isShowSuccessfulTests()); + showDisabledTestsCheckBox.setSelected(info.isShowDisabledTests()); + showTestDescriptionCheckBox.setSelected(info.isShowTestDescription()); + syncDetailTabCheckBox.setSelected(info.isSyncDetailTab()); + testPackagePrefixTextField.setText(info.getTestPackagePrefix()); + testPackageSuffixTextField.setText(info.getTestPackageSuffix()); + testUnitPrefixTextField.setText(info.getTestUnitPrefix()); + testUnitSuffixTextField.setText(info.getTestUnitSuffix()); + numberOfTestsPerUnitSpinner.setValue(info.getNumberOfTestsPerUnit()); + checkGenerateUtplsqlTestCheckBox.setSelected(info.isCheckGenerateUtplsqlTest()); + loadCodeTemplates(); + generateCommentsCheckBox.setSelected(info.isGenerateComments()); + disableTestsCheckBox.setSelected(info.isDisableTests()); + suitePathTextField.setText(info.getSuitePath()); + indentSpacesSpinner.setValue(info.getIndentSpaces()); + rootFolderInOddgenViewTextField.setText(info.getRootFolderInOddgenView()); + generateFilesCheckBox.setSelected(info.isGenerateFiles()); + outputDirectoryTextField.setText(info.getOutputDirectory()); + deleteExistingFilesCheckBox.setSelected(info.isDeleteExistingFiles()); + super.onEntry(traversableContext); + } + + @Override + public void onExit(final TraversableContext traversableContext) throws TraversalException { + PreferenceModel info = getUserInformation(traversableContext); + info.setUseRealtimeReporter(useRealtimeReporterCheckBox.isSelected()); + info.setUnsharedWorksheet(unsharedWorksheetCheckBox.isSelected()); + info.setResetPackage(resetPackageCheckBox.isSelected()); + info.setClearScreen(clearScreenCheckBox.isSelected()); + info.setAutoExecute(autoExecuteCheckBox.isSelected()); + info.setNumberOfRunsInHistory((Integer) numberOfRunsInHistorySpinner.getValue()); + info.setCheckRunUtplsqlTest(checkRunUtplsqlTestCheckBox.isSelected()); + info.setUseSmartTimes(useSmartTimesCheckBox.isSelected()); + info.setShowDisabledCounter(showDisabledCounterCheckBox.isSelected()); + info.setShowWarningsCounter(showWarningsCounterCheckBox.isSelected()); + info.setShowInfoCounter(showInfoCounterCheckBox.isSelected()); + info.setShowWarningIndicator(showWarningIndicatorCheckBox.isSelected()); + info.setShowInfoIndicator(showInfoIndicatorCheckBox.isSelected()); + info.setShowSuccessfulTests(showSuccessfulTestsCheckBox.isSelected()); + info.setShowDisabledTests(showDisabledTestsCheckBox.isSelected()); + info.setShowTestDescription(showTestDescriptionCheckBox.isSelected()); + info.setSyncDetailTab(syncDetailTabCheckBox.isSelected()); + info.setTestPackagePrefix(testPackagePrefixTextField.getText()); + info.setTestPackageSuffix(testPackageSuffixTextField.getText()); + info.setTestUnitPrefix(testUnitPrefixTextField.getText()); + info.setTestUnitSuffix(testUnitSuffixTextField.getText()); + info.setNumberOfTestsPerUnit((Integer) numberOfTestsPerUnitSpinner.getValue()); + info.setCheckGenerateUtplsqlTest(checkGenerateUtplsqlTestCheckBox.isSelected()); + info.setGenerateComments(generateCommentsCheckBox.isSelected()); + info.setDisableTests(disableTestsCheckBox.isSelected()); + info.setSuitePath(suitePathTextField.getText()); + info.setIndentSpaces((Integer) indentSpacesSpinner.getValue()); + info.setRootFolderInOddgenView(rootFolderInOddgenViewTextField.getText()); + info.setGenerateFiles(generateFilesCheckBox.isSelected()); + info.setOutputDirectory(outputDirectoryTextField.getText()); + info.setDeleteExistingFiles(deleteExistingFilesCheckBox.isSelected()); + super.onExit(traversableContext); + } + + private static PreferenceModel getUserInformation(final TraversableContext tc) { + return PreferenceModel.getInstance(tc.getPropertyStorage()); + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/preference/PreferencePanel.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/preference/PreferencePanel.xtend deleted file mode 100644 index 9abbf9f1..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/preference/PreferencePanel.xtend +++ /dev/null @@ -1,420 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.ui.preference - -import java.awt.event.ActionEvent -import java.awt.event.ActionListener -import java.util.Map -import javax.swing.JButton -import javax.swing.JCheckBox -import javax.swing.JOptionPane -import javax.swing.JPanel -import javax.swing.JSpinner -import javax.swing.JTabbedPane -import javax.swing.JTextField -import javax.swing.SpinnerNumberModel -import javax.swing.table.DefaultTableModel -import oracle.dbtools.raptor.templates.CodeTemplateUtil -import oracle.ide.panels.DefaultTraversablePanel -import oracle.ide.panels.TraversableContext -import oracle.ide.panels.TraversalException -import oracle.javatools.ui.layout.FieldLayoutBuilder -import org.utplsql.sqldev.model.preference.PreferenceModel -import org.utplsql.sqldev.resources.UtplsqlResources -import org.utplsql.sqldev.snippet.SnippetMerger -import org.utplsql.sqldev.ui.common.DirectoryChooser - -class PreferencePanel extends DefaultTraversablePanel { - val JPanel runTestPanel = new JPanel(); - val JCheckBox useRealtimeReporterCheckBox = new JCheckBox - val JCheckBox unsharedWorksheetCheckBox = new JCheckBox - val JCheckBox resetPackageCheckBox = new JCheckBox - val JCheckBox clearScreenCheckBox = new JCheckBox - val JCheckBox autoExecuteCheckBox = new JCheckBox - val JCheckBox checkRunUtplsqlTestCheckBox = new JCheckBox - val JCheckBox useSmartTimesCheckBox = new JCheckBox - val JButton importSnippetsButton = new JButton(UtplsqlResources.getString("PREF_IMPORT_SNIPPETS_BUTTON_LABEL")) - val JPanel realtimeReporterPanel = new JPanel - val SpinnerNumberModel numberOfRunsInHistoryModel = new SpinnerNumberModel(1, 1, 100, 1); - val JSpinner numberOfRunsInHistorySpinner = new JSpinner(numberOfRunsInHistoryModel); - val JCheckBox showDisabledCounterCheckBox = new JCheckBox - val JCheckBox showWarningsCounterCheckBox = new JCheckBox - val JCheckBox showInfoCounterCheckBox = new JCheckBox - val JCheckBox showWarningIndicatorCheckBox = new JCheckBox - val JCheckBox showInfoIndicatorCheckBox = new JCheckBox - val JCheckBox showSuccessfulTestsCheckBox = new JCheckBox - val JCheckBox showDisabledTestsCheckBox = new JCheckBox - val JCheckBox showTestDescriptionCheckBox = new JCheckBox - val JCheckBox syncDetailTabCheckBox = new JCheckBox - val JPanel generateTestPanel = new JPanel(); - val JTextField testPackagePrefixTextField = new JTextField - val JTextField testPackageSuffixTextField = new JTextField - val JTextField testUnitPrefixTextField = new JTextField - val JTextField testUnitSuffixTextField = new JTextField - val SpinnerNumberModel numberOfTestsPerUnitModel = new SpinnerNumberModel(1, 1, 10, 1); - val JSpinner numberOfTestsPerUnitSpinner = new JSpinner(numberOfTestsPerUnitModel); - val JCheckBox checkGenerateUtplsqlTestCheckBox = new JCheckBox - val DefaultTableModel codeTemplatesModel = new DefaultTableModel(#["Id", "Template"], 0); - val JButton createCodeTemplatesButton = new JButton() - val JCheckBox generateCommentsCheckBox = new JCheckBox - val JCheckBox disableTestsCheckBox = new JCheckBox - val JTextField suitePathTextField = new JTextField - val SpinnerNumberModel indentSpacesModel = new SpinnerNumberModel(1, 1, 8, 1); - val JSpinner indentSpacesSpinner = new JSpinner(indentSpacesModel); - val JPanel oddgenPanel = new JPanel(); - val JTextField rootFolderInOddgenViewTextField = new JTextField - val JCheckBox generateFilesCheckBox = new JCheckBox - val JTextField outputDirectoryTextField = new JTextField - val JButton outputDirectoryBrowse = new JButton() - val JCheckBox deleteExistingFilesCheckBox = new JCheckBox - - new() { - layoutControls() - } - - private def layoutControls() { - // run test group - val FieldLayoutBuilder runTab = new FieldLayoutBuilder(runTestPanel) - runTab.alignLabelsLeft = true - runTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_USE_REALTIME_REPORTER_LABEL")).component( - useRealtimeReporterCheckBox).withHint(UtplsqlResources.getString("PREF_USE_REALTIME_REPORTER_HINT"))) - runTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_UNSHARED_WORKSHEET_LABEL")).component( - unsharedWorksheetCheckBox)) - runTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_RESET_PACKAGE_LABEL")).component( - resetPackageCheckBox)) - runTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_CLEAR_SCREEN_LABEL")).component( - clearScreenCheckBox)) - runTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_AUTO_EXECUTE_LABEL")).component( - autoExecuteCheckBox)) - runTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_CHECK_RUN_UTPLSQL_TEST_LABEL")).component( - checkRunUtplsqlTestCheckBox)) - runTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_USE_SMART_TIMES_LABEL")).component( - useSmartTimesCheckBox)) - runTab.addVerticalGap - runTab.addRow(importSnippetsButton) - runTab.addVerticalSpring - - // realtime reporter group - val FieldLayoutBuilder rrTab = new FieldLayoutBuilder(realtimeReporterPanel) - rrTab.alignLabelsLeft = true - - rrTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_NUMBER_OF_RUNS_IN_HISTORY_LABEL")).component( - numberOfRunsInHistorySpinner)) - rrTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_SHOW_DISABLED_COUNTER_LABEL")).component( - showDisabledCounterCheckBox)) - rrTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_SHOW_WARNINGS_COUNTER_LABEL")).component( - showWarningsCounterCheckBox)) - rrTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_SHOW_INFO_COUNTER_LABEL")).component( - showInfoCounterCheckBox)) - rrTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_SHOW_WARNING_INDICATOR_LABEL")).component( - showWarningIndicatorCheckBox)) - rrTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_SHOW_INFO_INDICATOR_LABEL")).component( - showInfoIndicatorCheckBox)) - rrTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_SHOW_SUCCESSFUL_TESTS_LABEL")).component( - showSuccessfulTestsCheckBox)) - rrTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_SHOW_DISABLED_TESTS_LABEL")).component( - showDisabledTestsCheckBox)) - rrTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_SHOW_TEST_DESCRIPTION_LABEL")).component( - showTestDescriptionCheckBox)) - rrTab.add( - runTab.field.label.withText(UtplsqlResources.getString("PREF_SYNC_DETAIL_TAB_LABEL")).component( - syncDetailTabCheckBox)) - rrTab.addVerticalSpring - - // generate test group - val FieldLayoutBuilder generateTab = new FieldLayoutBuilder(generateTestPanel) - generateTab.alignLabelsLeft = true - generateTab.stretchComponentsWithNoButton = true - generateTab.add( - generateTab.field.label.withText(UtplsqlResources.getString("PREF_TEST_PACKAGE_PREFIX_LABEL")).component( - testPackagePrefixTextField)) - generateTab.add( - generateTab.field.label.withText(UtplsqlResources.getString("PREF_TEST_PACKAGE_SUFFIX_LABEL")).component( - testPackageSuffixTextField)) - generateTab.add( - generateTab.field.label.withText(UtplsqlResources.getString("PREF_TEST_UNIT_PREFIX_LABEL")).component( - testUnitPrefixTextField)) - generateTab.add( - generateTab.field.label.withText(UtplsqlResources.getString("PREF_TEST_UNIT_SUFFIX_LABEL")).component( - testUnitSuffixTextField)) - generateTab.add( - generateTab.field.label.withText(UtplsqlResources.getString("PREF_NUMBER_OF_TESTS_PER_UNIT_LABEL")).component( - numberOfTestsPerUnitSpinner)) - generateTab.add( - generateTab.field.label.withText(UtplsqlResources.getString("PREF_GENERATE_COMMENTS_LABEL")).component( - generateCommentsCheckBox)) - generateTab.add( - generateTab.field.label.withText(UtplsqlResources.getString("PREF_DISABLE_TESTS_LABEL")).component( - disableTestsCheckBox)) - generateTab.add( - generateTab.field.label.withText(UtplsqlResources.getString("PREF_SUITE_PATH_LABEL")).component( - suitePathTextField)) - generateTab.add( - generateTab.field.label.withText(UtplsqlResources.getString("PREF_INDENT_SPACES_LABEL")).component( - indentSpacesSpinner)) - generateTab.add( - generateTab.field.label.withText(UtplsqlResources.getString("PREF_CHECK_GENERATE_UTPLSQL_TEST_LABEL")).component( - checkGenerateUtplsqlTestCheckBox).button(createCodeTemplatesButton).withText( - UtplsqlResources.getString("PREF_CREATE_CODE_TEMPLATES_BUTTON_LABEL"))) - generateTab.addVerticalSpring - - // oddgen group - val FieldLayoutBuilder oddgenTab = new FieldLayoutBuilder(oddgenPanel) - oddgenTab.alignLabelsLeft = true - oddgenTab.stretchComponentsWithNoButton = true - oddgenTab.add( - oddgenTab.field.label.withText(UtplsqlResources.getString("PREF_ROOT_FOLDER_IN_ODDGEN_VIEW_LABEL")).component( - rootFolderInOddgenViewTextField)) - oddgenTab.add( - oddgenTab.field.label.withText(UtplsqlResources.getString("PREF_GENERATE_FILES_LABEL")).component( - generateFilesCheckBox)) - oddgenTab.add( - oddgenTab.field.label.withText(UtplsqlResources.getString("PREF_OUTPUT_DIRECTORY_LABEL")).component( - outputDirectoryTextField).button(outputDirectoryBrowse).withText( - UtplsqlResources.getString("PREF_OUTPUT_DIRECTORY_BUTTON_LABEL"))) - oddgenTab.add( - oddgenTab.field.label.withText(UtplsqlResources.getString("PREF_DELETE_EXISTING_FILES_LABEL")).component( - deleteExistingFilesCheckBox)) - oddgenTab.addVerticalSpring - - // putting groups into tabbed panes - val tabbedPane = new JTabbedPane() - tabbedPane.add(UtplsqlResources.getString("MENU_RUN_TEST_LABEL"), runTestPanel) - tabbedPane.add(UtplsqlResources.getString("MENU_REALTIME_REPORTER_LABEL"), realtimeReporterPanel) - tabbedPane.add(UtplsqlResources.getString("MENU_GENERATE_TEST_LABEL"), generateTestPanel) - tabbedPane.add("oddgen", oddgenPanel) - val FieldLayoutBuilder builder = new FieldLayoutBuilder(this) - builder.alignLabelsLeft = true - builder.addVerticalField("", tabbedPane) - builder.addVerticalSpring - - // register action listener for import snippets button - importSnippetsButton.addActionListener(new ActionListener() { - override actionPerformed(ActionEvent event) { - importSnippets - } - }) - - // register action listener for create code template button - createCodeTemplatesButton.addActionListener(new ActionListener() { - override actionPerformed(ActionEvent event) { - saveCodeTemplates - } - }) - - // register action listener for directory chooser - outputDirectoryBrowse.addActionListener(new ActionListener() { - override actionPerformed(ActionEvent event) { - DirectoryChooser.choose(null, UtplsqlResources.getString("PREF_OUTPUT_DIRECTORY_LABEL"), - outputDirectoryTextField) - } - }) - } - - private def importSnippets() { - val snippetMerger = new SnippetMerger - snippetMerger.merge - val file = snippetMerger.file.absolutePath - val message = String.format(UtplsqlResources.getString("PREF_CONFIRM_IMPORT_MESSAGE"), file) - JOptionPane.showMessageDialog(null, message, UtplsqlResources.getString("PREF_CONFIRM_IMPORT_TITLE"), - JOptionPane.INFORMATION_MESSAGE); - } - - private def loadCodeTemplates() { - val Map map = CodeTemplateUtil.loadFiles() - for (key : map.keySet) { - codeTemplatesModel.addRow(#[key, map.get(key)]) - } - } - - private def saveCodeTemplates() { - codeTemplatesModel.addRow(#["ut_spec", utSpecTemplate.replaceTabsWithSpaces]) - codeTemplatesModel.addRow(#["ut_spec_proc", utSpecProcTemplate.replaceTabsWithSpaces.trimPlusNewLine]) - codeTemplatesModel.addRow(#["ut_body", utBodyTemplate.replaceTabsWithSpaces]) - codeTemplatesModel.addRow(#["ut_body_proc", utBodyProcTemplate.replaceTabsWithSpaces.trimPlusNewLine]) - CodeTemplateUtil.save(codeTemplatesModel) - } - - private def replaceTabsWithSpaces(CharSequence input) { - val spaces = String.format("%1$"+indentSpacesSpinner.value+"s", "") - return input.toString.replace("\t", spaces) - } - - private def trimPlusNewLine(String input) { - input.trim + System.lineSeparator - } - - private def utSpecTemplate() ''' - CREATE OR REPLACE PACKAGE «testPackagePrefixTextField.text»[package_name]«testPackageSuffixTextField.text» IS - - --%suite - «IF !suitePathTextField.text.empty» - --%suitepath(«suitePathTextField.text») - «ENDIF» - - «utSpecProcTemplate» - END «testPackagePrefixTextField.text»[package_name]«testPackageSuffixTextField.text»; - / - ''' - - private def utSpecProcTemplate() ''' - «val withContext = numberOfTestsPerUnitModel.value as Integer > 1» - «IF withContext» - --%context([procedure_name]) - - «ENDIF» - «FOR i : 1 .. numberOfTestsPerUnitModel.value as Integer» - --%test - «IF disableTestsCheckBox.selected» - --%disabled - «ENDIF» - PROCEDURE «testUnitPrefixTextField.text»[procedure_name]«testUnitSuffixTextField.text»«IF withContext»«i»«ENDIF»; - - «ENDFOR» - «IF withContext» - --%endcontext - - «ENDIF» - ''' - - private def utBodyTemplate() ''' - CREATE OR REPLACE PACKAGE BODY «testPackagePrefixTextField.text»[package_name]«testPackageSuffixTextField.text» IS - - «utBodyProcTemplate» - END «testPackagePrefixTextField.text»[package_name]«testPackageSuffixTextField.text»; - / - ''' - - private def utBodyProcTemplate() ''' - «val withContext = numberOfTestsPerUnitModel.value as Integer > 1» - «FOR i : 1 .. numberOfTestsPerUnitModel.value as Integer» - «IF generateCommentsCheckBox.selected» - -- - -- test«IF withContext» [procedure_name] case «i»: ...«ENDIF» - -- - «ENDIF» - PROCEDURE «testUnitPrefixTextField.text»[procedure_name]«testUnitSuffixTextField.text»«IF withContext»«i»«ENDIF» IS - l_actual INTEGER := 0; - l_expected INTEGER := 1; - BEGIN - «IF generateCommentsCheckBox.selected» - -- populate actual - -- ... - - -- populate expected - -- ... - - -- assert - «ENDIF» - ut.expect(l_actual).to_equal(l_expected); - END «testUnitPrefixTextField.text»[procedure_name]«testUnitSuffixTextField.text»«IF withContext»«i»«ENDIF»; - - «ENDFOR» - ''' - - override onEntry(TraversableContext traversableContext) { - var PreferenceModel info = traversableContext.userInformation - useRealtimeReporterCheckBox.selected = info.useRealtimeReporter - unsharedWorksheetCheckBox.selected = info.unsharedWorksheet - resetPackageCheckBox.selected = info.resetPackage - clearScreenCheckBox.selected = info.clearScreen - autoExecuteCheckBox.selected = info.autoExecute - checkRunUtplsqlTestCheckBox.selected = info.checkRunUtplsqlTest - useSmartTimesCheckBox.selected = info.useSmartTimes - numberOfRunsInHistorySpinner.value = info.numberOfRunsInHistory - showDisabledCounterCheckBox.selected = info.showDisabledCounter - showWarningsCounterCheckBox.selected = info.showWarningsCounter - showInfoCounterCheckBox.selected = info.showInfoCounter - showWarningIndicatorCheckBox.selected = info.showWarningIndicator - showInfoIndicatorCheckBox.selected = info.showInfoIndicator - showSuccessfulTestsCheckBox.selected = info.showSuccessfulTests - showDisabledTestsCheckBox.selected = info.showDisabledTests - showTestDescriptionCheckBox.selected = info.showTestDescription - syncDetailTabCheckBox.selected = info.syncDetailTab - testPackagePrefixTextField.text = info.testPackagePrefix - testPackageSuffixTextField.text = info.testPackageSuffix - testUnitPrefixTextField.text = info.testUnitPrefix - testUnitSuffixTextField.text = info.testUnitSuffix - numberOfTestsPerUnitSpinner.value = info.numberOfTestsPerUnit - checkGenerateUtplsqlTestCheckBox.selected = info.checkGenerateUtplsqlTest - loadCodeTemplates - generateCommentsCheckBox.selected = info.generateComments - disableTestsCheckBox.selected = info.disableTests - suitePathTextField.text = info.suitePath - indentSpacesSpinner.value = info.indentSpaces - rootFolderInOddgenViewTextField.text = info.rootFolderInOddgenView - generateFilesCheckBox.selected = info.generateFiles - outputDirectoryTextField.text = info.outputDirectory - deleteExistingFilesCheckBox.selected = info.deleteExistingFiles - super.onEntry(traversableContext) - } - - override onExit(TraversableContext traversableContext) throws TraversalException { - var PreferenceModel info = traversableContext.userInformation - info.useRealtimeReporter = useRealtimeReporterCheckBox.selected - info.unsharedWorksheet = unsharedWorksheetCheckBox.selected - info.resetPackage = resetPackageCheckBox.selected - info.clearScreen = clearScreenCheckBox.selected - info.autoExecute = autoExecuteCheckBox.selected - info.numberOfRunsInHistory = numberOfRunsInHistorySpinner.value as Integer - info.checkRunUtplsqlTest = checkRunUtplsqlTestCheckBox.selected - info.useSmartTimes = useSmartTimesCheckBox.selected - info.showDisabledCounter = showDisabledCounterCheckBox.selected - info.showWarningsCounter = showWarningsCounterCheckBox.selected - info.showInfoCounter = showInfoCounterCheckBox.selected - info.showWarningIndicator = showWarningIndicatorCheckBox.selected - info.showInfoIndicator = showInfoIndicatorCheckBox.selected - info.showSuccessfulTests = showSuccessfulTestsCheckBox.selected - info.showDisabledTests = showDisabledTestsCheckBox.selected - info.showTestDescription = showTestDescriptionCheckBox.selected - info.syncDetailTab = syncDetailTabCheckBox.selected - info.testPackagePrefix = testPackagePrefixTextField.text - info.testPackageSuffix = testPackageSuffixTextField.text - info.testUnitPrefix = testUnitPrefixTextField.text - info.testUnitSuffix = testUnitSuffixTextField.text - info.numberOfTestsPerUnit = numberOfTestsPerUnitSpinner.value as Integer - info.checkGenerateUtplsqlTest = checkGenerateUtplsqlTestCheckBox.selected - info.generateComments = generateCommentsCheckBox.selected - info.disableTests = disableTestsCheckBox.selected - info.suitePath = suitePathTextField.text - info.indentSpaces = indentSpacesSpinner.value as Integer - info.rootFolderInOddgenView = rootFolderInOddgenViewTextField.text - info.generateFiles = generateFilesCheckBox.selected - info.outputDirectory = outputDirectoryTextField.text - info.deleteExistingFiles = deleteExistingFilesCheckBox.selected - super.onExit(traversableContext) - } - - private def static PreferenceModel getUserInformation(TraversableContext tc) { - return PreferenceModel.getInstance(tc.propertyStorage) - } -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/ComboBoxItem.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/ComboBoxItem.java similarity index 60% rename from sqldev/src/main/java/org/utplsql/sqldev/ui/runner/ComboBoxItem.xtend rename to sqldev/src/main/java/org/utplsql/sqldev/ui/runner/ComboBoxItem.java index f78658a3..ac8f701b 100644 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/ComboBoxItem.xtend +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/ComboBoxItem.java @@ -13,16 +13,19 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.utplsql.sqldev.ui.runner +package org.utplsql.sqldev.ui.runner; -import java.util.AbstractMap +import java.util.AbstractMap; -class ComboBoxItem extends AbstractMap.SimpleEntry { - new(K key, V value) { - super(key, value) - } - - override toString() { - return value.toString - } +public class ComboBoxItem extends AbstractMap.SimpleEntry { + private static final long serialVersionUID = 7869442222989031548L; + + public ComboBoxItem(final K key, final V value) { + super(key, value); + } + + @Override + public String toString() { + return this.getValue().toString(); + } } diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/FailuresTableModel.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/FailuresTableModel.java new file mode 100644 index 00000000..3acf505c --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/FailuresTableModel.java @@ -0,0 +1,93 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.runner; + +import java.util.Arrays; +import java.util.List; + +import javax.swing.table.DefaultTableModel; + +import org.utplsql.sqldev.model.runner.Expectation; +import org.utplsql.sqldev.resources.UtplsqlResources; + +public class FailuresTableModel extends DefaultTableModel { + private static final long serialVersionUID = 8119453059788497567L; + private List failedExpectations; + private List columnNames = Arrays.asList("#", UtplsqlResources.getString("RUNNER_ASSERT_DESCRIPTION_COLUMN")); + + public FailuresTableModel() { + super(); + } + + public void setModel(final List failedExpectations) { + this.failedExpectations = failedExpectations; + } + + public Expectation getExpectation(final int row) { + return failedExpectations.get(row); + } + + @Override + public int getRowCount() { + if (failedExpectations == null) { + return 0; + } + return failedExpectations.size(); + } + + @Override + public int getColumnCount() { + return 2; + } + + @Override + public Object getValueAt(final int row, final int col) { + final Expectation expectation = failedExpectations.get(row); + if (expectation == null) { + return null; + } + switch (col) { + case 0: + return row + 1; + case 1: + return expectation.getShortFailureText(); + default: + return null; + } + } + + @Override + public String getColumnName(final int col) { + return columnNames.get(col); + } + + @Override + public boolean isCellEditable(final int row, final int column) { + return false; + } + + @Override + public Class getColumnClass(final int col) { + switch (col) { + case 0: + return Integer.class; + case 1: + return String.class; + default: + return String.class; + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/FailuresTableModel.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/FailuresTableModel.xtend deleted file mode 100644 index acd9418b..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/FailuresTableModel.xtend +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.utplsql.sqldev.ui.runner - -import java.util.List -import javax.swing.table.DefaultTableModel -import org.utplsql.sqldev.model.runner.Expectation -import org.utplsql.sqldev.resources.UtplsqlResources - -class FailuresTableModel extends DefaultTableModel { - List failedExpectations - - new() { - super() - } - - def setModel(List failedExpectations) { - this.failedExpectations = failedExpectations - } - - def getExpectation(int row) { - return failedExpectations.get(row) - } - - override getRowCount() { - if (failedExpectations === null) { - return 0 - } - return failedExpectations.size() - } - - override getColumnCount() { - return 2 - } - - override getValueAt(int row, int col) { - val expectation = failedExpectations.get(row) - if (expectation === null) { - return null - } - switch (col) { - case 0: { - return row + 1 - } - case 1: { - return expectation.shortFailureText - } - default: { - return null - } - } - } - - override getColumnName(int col) { - return #["#", UtplsqlResources.getString("RUNNER_ASSERT_DESCRIPTION_COLUMN")].get(col) - } - - override isCellEditable(int row, int column) { - return false - } - - override getColumnClass(int col) { - switch (col) { - case 0: { - return Integer - } - case 1: { - return String - } - default: { - return String - } - } - } -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/GradientToolbar.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/GradientToolbar.java new file mode 100644 index 00000000..6f163124 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/GradientToolbar.java @@ -0,0 +1,85 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.runner; + +import java.awt.Color; +import java.awt.GradientPaint; +import java.awt.Graphics; +import java.awt.Graphics2D; +import java.awt.Insets; + +import javax.swing.JToolBar; +import javax.swing.LookAndFeel; +import javax.swing.UIManager; +import javax.swing.border.BevelBorder; +import javax.swing.border.EmptyBorder; + +public class GradientToolbar extends JToolBar { + private static final long serialVersionUID = 6128707792081725058L; + + private boolean isOracleLookAndFeel() { + LookAndFeel laf = UIManager.getLookAndFeel(); + final String lafName = laf != null ? laf.getName() : null; + return "Oracle Look and Feel version 2".equals(lafName); + } + + public GradientToolbar() { + super(); + if (isOracleLookAndFeel()) { + setBorder(new EmptyBorder(new Insets(2, 2, 2, 2))); // insets: top, left, bottom, right + } else { + setBorder(new BevelBorder(BevelBorder.RAISED)); + } + } + + @Override + public void paintComponent(final Graphics g) { + if (isOracleLookAndFeel()) { + // emulate Oracle toolbar + // 1. default for non-opaque components + if (!isOpaque()) { + super.paintComponent(g); + return; + } + + // 2. paint gradient background from top to bottom with separator line at the bottom + final Graphics2D g2d = ((Graphics2D) g); + final int w = getWidth(); + final int h = (getHeight() - 1); + final int h2 = (getHeight() / 2); + final Color colorTop = new Color(237, 237, 237); + final Color colorMiddle = new Color(244, 244, 244); + final Color colorBottom = new Color(254, 254, 254); + final Color colorBottomLine = Color.LIGHT_GRAY; + final GradientPaint gp1 = new GradientPaint(0, 0, colorTop, 0, h2, colorMiddle); + g2d.setPaint(gp1); + g2d.fillRect(0, 0, w, h2); + final GradientPaint gp2 = new GradientPaint(0, h2, colorMiddle, 0, h, colorBottom); + g2d.setPaint(gp2); + g2d.fillRect(0, h2, w, h); + g2d.setPaint(colorBottomLine); + g2d.fillRect(0, h, w, (h + 1)); + + // 3. do rest, changing opaque to ensure background is not overwritten + setOpaque(false); + super.paintComponent(g); + setOpaque(true); + } else { + // default logic + super.paintComponent(g); + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/GradientToolbar.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/GradientToolbar.xtend deleted file mode 100644 index 8c04e7b5..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/GradientToolbar.xtend +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.ui.runner - -import java.awt.Color -import java.awt.GradientPaint -import java.awt.Graphics -import java.awt.Graphics2D -import java.awt.Insets -import javax.swing.JToolBar -import javax.swing.UIManager -import javax.swing.border.BevelBorder -import javax.swing.border.EmptyBorder - -class GradientToolbar extends JToolBar { - - private def isOracleLookAndFeel() { - val laf = UIManager.lookAndFeel?.name - if (laf == "Oracle Look and Feel version 2") { - return true - } else { - return false - } - } - - new() { - super() - if (oracleLookAndFeel) { - this.border = new EmptyBorder(new Insets(2, 2, 2, 2)) // top, left, bottom, right - } else { - this.border = new BevelBorder(BevelBorder.RAISED) - } - } - - override paintComponent(Graphics g) { - if (oracleLookAndFeel) { - // emulate Oracle toolbar - // 1. default for non-opaque components - if (!opaque) { - super.paintComponent(g) - return - } - - // 2. paint gradient background from top to bottom with separator line at the bottom - val g2d = g as Graphics2D - val w = width - val h = height - 1 - val int h2 = height / 2 - val colorTop = new Color(237, 237, 237) - val colorMiddle = new Color(244, 244, 244) - val colorBottom = new Color(254, 254, 254) - val colorBottomLine = Color.LIGHT_GRAY - val gp1 = new GradientPaint(0, 0, colorTop, 0, h2, colorMiddle) - g2d.paint = gp1 - g2d.fillRect(0, 0, w, h2) - val gp2 = new GradientPaint(0, h2, colorMiddle, 0, h, colorBottom) - g2d.paint = gp2 - g2d.fillRect(0, h2, w, h) - g2d.paint = colorBottomLine - g2d.fillRect(0, h, w, h+1) - - // 3. do rest, changing opaque to ensure background is not overwritten - setOpaque(false) - super.paintComponent(g) - setOpaque(true) - } else { - // default logic - super.paintComponent(g) - } - } - -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerFactory.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerFactory.java new file mode 100644 index 00000000..72483953 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerFactory.java @@ -0,0 +1,64 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.runner; + +import oracle.ide.docking.DockStation; +import oracle.ide.docking.Dockable; +import oracle.ide.docking.DockableFactory; +import oracle.ide.docking.DockingParam; +import oracle.ide.layout.ViewId; + +public class RunnerFactory implements DockableFactory { + public static final String FACTORY_NAME = "UTPLSQL_RUNNER_FACTORY"; + + private RunnerView dockable; + + @Override + public void install() { + final DockStation dockStation = DockStation.getDockStation(); + final DockingParam dp = new DockingParam(); + final ViewId referencedViewId = new ViewId("DatabaseNavigatorWindow", "DatabaseNavigatorWindow"); + final Dockable referencedDockable = dockStation.findDockable(referencedViewId); + dp.setTabbedWith(referencedDockable); + dockStation.dock(getLocalDockable(), dp); + } + + @Override + public Dockable getDockable(final ViewId viewId) { + if (viewId == RunnerView.VIEW_ID) { + return getLocalDockable(); + } + return null; + } + + private RunnerView getLocalDockable() { + if (dockable == null) { + dockable = new RunnerView(); + } + return dockable; + } + + public static RunnerView getDockable() { + final DockStation dockStation = DockStation.getDockStation(); + final Dockable dockable = dockStation.findDockable(RunnerView.VIEW_ID); + return (RunnerView) dockable; + } + + public static void showDockable() { + final DockStation dockStation = DockStation.getDockStation(); + dockStation.setDockableVisible(getDockable(), true); + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerFactory.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerFactory.xtend deleted file mode 100644 index 61915559..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerFactory.xtend +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.ui.runner - -import oracle.ide.docking.DockStation -import oracle.ide.docking.DockableFactory -import oracle.ide.docking.DockingParam -import oracle.ide.layout.ViewId - -class RunnerFactory implements DockableFactory { - public static val FACTORY_NAME = "UTPLSQL_RUNNER_FACTORY" - - var RunnerView dockable - - override install() { - val dockStation = DockStation.getDockStation(); - val dp = new DockingParam(); - val referencedViewId = new ViewId("DatabaseNavigatorWindow", "DatabaseNavigatorWindow") - val referencedDockable = dockStation.findDockable(referencedViewId) - dp.tabbedWith = referencedDockable - dockStation.dock(getLocalDockable(), dp); - } - - override getDockable(ViewId viewId) { - if (viewId === RunnerView.VIEW_ID) { - return localDockable - } - return null - } - - private def getLocalDockable() { - if (dockable === null) { - dockable = new RunnerView - } - return dockable - } - - static def getDockable() { - val dockStation = DockStation.dockStation - val dockable = dockStation.findDockable(RunnerView.VIEW_ID) - return dockable as RunnerView - } - - static def void showDockable() { - val dockStation = DockStation.dockStation - dockStation.setDockableVisible(getDockable(), true) - } -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerPanel.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerPanel.java new file mode 100644 index 00000000..783b6415 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerPanel.java @@ -0,0 +1,1322 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.runner; + +import java.awt.Color; +import java.awt.Component; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Insets; +import java.awt.Rectangle; +import java.awt.event.ActionListener; +import java.awt.event.MouseAdapter; +import java.awt.event.MouseEvent; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.DefaultComboBoxModel; +import javax.swing.JCheckBoxMenuItem; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JMenuItem; +import javax.swing.JPanel; +import javax.swing.JPopupMenu; +import javax.swing.JProgressBar; +import javax.swing.JScrollPane; +import javax.swing.JSeparator; +import javax.swing.JSplitPane; +import javax.swing.JTabbedPane; +import javax.swing.JTable; +import javax.swing.LookAndFeel; +import javax.swing.RepaintManager; +import javax.swing.RowFilter; +import javax.swing.SwingConstants; +import javax.swing.Timer; +import javax.swing.UIManager; +import javax.swing.border.Border; +import javax.swing.border.CompoundBorder; +import javax.swing.border.EmptyBorder; +import javax.swing.event.HyperlinkEvent; +import javax.swing.plaf.basic.BasicProgressBarUI; +import javax.swing.table.DefaultTableCellRenderer; +import javax.swing.table.TableCellRenderer; +import javax.swing.table.TableColumn; +import javax.swing.table.TableRowSorter; + +import org.springframework.web.util.HtmlUtils; +import org.utplsql.sqldev.dal.UtplsqlDao; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.LimitedLinkedHashMap; +import org.utplsql.sqldev.model.StringTools; +import org.utplsql.sqldev.model.SystemTools; +import org.utplsql.sqldev.model.preference.PreferenceModel; +import org.utplsql.sqldev.model.runner.Counter; +import org.utplsql.sqldev.model.runner.Expectation; +import org.utplsql.sqldev.model.runner.Run; +import org.utplsql.sqldev.model.runner.Test; +import org.utplsql.sqldev.parser.UtplsqlParser; +import org.utplsql.sqldev.resources.UtplsqlResources; +import org.utplsql.sqldev.runner.UtplsqlRunner; +import org.utplsql.sqldev.runner.UtplsqlWorksheetRunner; + +import oracle.dbtools.raptor.controls.grid.DefaultDrillLink; +import oracle.ide.config.Preferences; +import oracle.javatools.ui.table.ToolbarButton; + +public class RunnerPanel { + private static final Color GREEN = new Color(0, 153, 0); + private static final Color RED = new Color(153, 0, 0); + private static final int INDICATOR_WIDTH = 20; + private static final int OVERVIEW_TABLE_ROW_HEIGHT = 20; + private static final Dimension TEXTPANE_DIM = new Dimension(100, 100); + + private boolean useSmartTimes = false; + private LimitedLinkedHashMap runs = new LimitedLinkedHashMap<>(10); + private Run currentRun; + private JPanel basePanel; + private DefaultComboBoxModel> runComboBoxModel; + private JComboBox> runComboBox; + private JLabel statusLabel; + private Timer elapsedTimeTimer; + private JLabel testCounterValueLabel; + private JLabel errorCounterValueLabel; + private JLabel failureCounterValueLabel; + private JLabel disabledCounterValueLabel; + private JLabel warningsCounterValueLabel; + private JLabel infoCounterValueLabel; + private JCheckBoxMenuItem showDisabledCounterCheckBoxMenuItem; + private JCheckBoxMenuItem showWarningsCounterCheckBoxMenuItem; + private JCheckBoxMenuItem showInfoCounterCheckBoxMenuItem; + private JProgressBar progressBar; + private TestOverviewTableModel testOverviewTableModel; + private JTable testOverviewTable; + private JMenuItem testOverviewRunMenuItem; + private JMenuItem testOverviewRunWorksheetMenuItem; + private JCheckBoxMenuItem showTestDescriptionCheckBoxMenuItem; + private JCheckBoxMenuItem showWarningIndicatorCheckBoxMenuItem; + private JCheckBoxMenuItem showInfoIndicatorCheckBoxMenuItem; + private JCheckBoxMenuItem showSuccessfulTestsCheckBoxMenuItem; + private JCheckBoxMenuItem showDisabledTestsCheckBoxMenuItem; + private JCheckBoxMenuItem syncDetailTabCheckBoxMenuItem; + private RunnerTextField testOwnerTextField; + private RunnerTextField testPackageTextField; + private RunnerTextField testProcedureTextField; + private RunnerTextArea testDescriptionTextArea; + private RunnerTextArea testIdTextArea; + private RunnerTextField testStartTextField; + private FailuresTableModel failuresTableModel; + private JTable failuresTable; + private RunnerTextPane testFailureMessageTextPane; + private RunnerTextPane testErrorStackTextPane; + private RunnerTextPane testWarningsTextPane; + private RunnerTextPane testServerOutputTextPane; + private JTabbedPane testDetailTabbedPane; + + // used in multiple components, therefore an inner class + private class TestTableHeaderRenderer extends DefaultTableCellRenderer { + private static final long serialVersionUID = 6295858563570577027L; + + @Override + public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, + final boolean hasFocus, final int row, final int col) { + final TableCellRenderer renderer = table.getTableHeader().getDefaultRenderer(); + final JLabel label = ((JLabel) renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, + row, col)); + if (col == 0) { + label.setIcon(UtplsqlResources.getIcon("STATUS_ICON")); + label.setHorizontalAlignment(JLabel.CENTER); + } else if (col == 1) { + label.setIcon(UtplsqlResources.getIcon("WARNING_ICON")); + label.setHorizontalAlignment(JLabel.CENTER); + } else if (col == 2) { + label.setIcon(UtplsqlResources.getIcon("INFO_ICON")); + label.setHorizontalAlignment(JLabel.CENTER); + } else if (col == 3) { + label.setIcon(null); + label.setHorizontalAlignment(JLabel.LEFT); + } else if (col == 4) { + label.setIcon(null); + label.setHorizontalAlignment(JLabel.RIGHT); + } + return label; + } + } + + // used in mulitple components, therefore an inner class + private class FailuresTableHeaderRenderer extends DefaultTableCellRenderer { + private static final long serialVersionUID = 5059401447983514596L; + + @Override + public Component getTableCellRendererComponent(final JTable table, final Object value, final boolean isSelected, + final boolean hasFocus, final int row, final int col) { + final TableCellRenderer renderer = table.getTableHeader().getDefaultRenderer(); + final JLabel label = ((JLabel) renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, + row, col)); + if (col == 0) { + label.setHorizontalAlignment(JLabel.RIGHT); + } else { + label.setHorizontalAlignment(JLabel.LEFT); + } + return label; + } + } + + public Component getGUI() { + if (basePanel == null) { + initializeGUI(); + } + if (!basePanel.isShowing()) { + applyPreferences(); + } + return basePanel; + } + + private void resetDerived() { + testOverviewTable.getRowSorter().setSortKeys(null); + testOverviewRunMenuItem.setEnabled(false); + testOverviewRunWorksheetMenuItem.setEnabled(false); + testIdTextArea.setText(null); + testOwnerTextField.setText(null); + testPackageTextField.setText(null); + testProcedureTextField.setText(null); + testDescriptionTextArea.setText(null); + testStartTextField.setText(null); + failuresTableModel.setModel(null); + failuresTableModel.fireTableDataChanged(); + testFailureMessageTextPane.setText(null); + testErrorStackTextPane.setText(null); + testWarningsTextPane.setText(null); + testServerOutputTextPane.setText(null); + } + + private void refreshRunsComboBox() { + if (!runs.isEmpty()) { + for (ActionListener al : runComboBox.getActionListeners()) { + runComboBox.removeActionListener(al); + } + runComboBoxModel.removeAllElements(); + List> entries = new ArrayList<>(runs.entrySet()); + for (int i = runs.size() - 1; i >= 0; i--) { + final Map.Entry entry = entries.get(i); + final ComboBoxItem item = new ComboBoxItem<>(entry.getKey(), entry.getValue().getName()); + runComboBoxModel.addElement(item); + } + runComboBox.setSelectedIndex(0); + runComboBox.addActionListener(event -> comboBoxAction()); + } + } + + private void applyShowNumberOfRunsInHistory(final int maxRuns) { + if (maxRuns != runs.getMaxEntries()) { + final LimitedLinkedHashMap newRuns = new LimitedLinkedHashMap<>(maxRuns); + for (final Map.Entry entry : runs.entrySet()) { + newRuns.put(entry.getKey(), entry.getValue()); + } + runs = newRuns; + } + } + + private void applyShowDisabledCounter() { + disabledCounterValueLabel.getParent().setVisible(showDisabledCounterCheckBoxMenuItem.isSelected()); + } + + private void applyShowWarningsCounter() { + warningsCounterValueLabel.getParent().setVisible(showWarningsCounterCheckBoxMenuItem.isSelected()); + } + + private void applyShowInfoCounter() { + infoCounterValueLabel.getParent().setVisible(showInfoCounterCheckBoxMenuItem.isSelected()); + } + + private void applyShowTestDescription() { + testOverviewTableModel.updateModel(showTestDescriptionCheckBoxMenuItem.isSelected()); + final TableColumn idColumn = testOverviewTable.getColumnModel().getColumn(3); + idColumn.setHeaderValue(testOverviewTableModel.getTestIdColumnName()); + testOverviewTable.getTableHeader().repaint(); + } + + private void applyShowWarningIndicator(final boolean show) { + final TableColumn col = testOverviewTable.getColumnModel().getColumn(1); + if (show) { + col.setWidth(INDICATOR_WIDTH); + col.setMinWidth(INDICATOR_WIDTH); + col.setMaxWidth(INDICATOR_WIDTH); + col.setPreferredWidth(INDICATOR_WIDTH); + } else { + col.setWidth(0); + col.setMinWidth(0); + col.setMaxWidth(0); + col.setPreferredWidth(0); + } + } + + private void applyShowInfoIndicator(final boolean show) { + final TableColumn col = testOverviewTable.getColumnModel().getColumn(2); + if (show) { + col.setWidth(INDICATOR_WIDTH); + col.setMinWidth(INDICATOR_WIDTH); + col.setMaxWidth(INDICATOR_WIDTH); + col.setPreferredWidth(INDICATOR_WIDTH); + } else { + col.setWidth(0); + col.setMinWidth(0); + col.setMaxWidth(0); + col.setPreferredWidth(0); + } + } + + private void applyFilter(final boolean showSuccessfulTests, final boolean showDisabledTests) { + @SuppressWarnings("unchecked") + final TableRowSorter sorter = ((TableRowSorter) testOverviewTable.getRowSorter()); + final RowFilter filter = new RowFilter() { + @Override + public boolean include(final RowFilter.Entry entry) { + final Test test = entry.getModel().getTest((entry.getIdentifier()).intValue()); + final Counter counter = test.getCounter(); + if (counter != null) { + if (counter.getSuccess() > 0 && !showSuccessfulTests) { + return false; + } + if (counter.getDisabled() > 0 && !showDisabledTests) { + return false; + } + } + return true; + } + }; + sorter.setRowFilter(filter); + } + + private void openTest(final Test test) { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(currentRun.getConnectionName())); + final String source = dao.getSource(test.getOwnerName(), "PACKAGE", test.getObjectName().toUpperCase()).trim(); + final UtplsqlParser parser = new UtplsqlParser(source); + final int line = parser.getLineOf(test.getProcedureName()); + openEditor(test.getOwnerName(), "PACKAGE", test.getObjectName().toUpperCase(), line, 1); + } + + private void openSelectedTest() { + final int rowIndex = testOverviewTable.getSelectedRow(); + if (rowIndex != -1) { + final int row = testOverviewTable.convertRowIndexToModel(rowIndex); + final Test test = testOverviewTableModel.getTest(row); + openTest(test); + } + } + + private void openSelectedFailure() { + final int rowIndex = failuresTable.getSelectedRow(); + if (rowIndex != -1) { + final int row = failuresTable.convertRowIndexToModel(rowIndex); + final Expectation expectation = failuresTableModel.getExpectation(row); + final Test test = testOverviewTableModel + .getTest(testOverviewTable.convertRowIndexToModel(testOverviewTable.getSelectedRow())); + final Integer callerLine = expectation.getCallerLine(); + if (callerLine != null) { + openEditor(test.getOwnerName(), "PACKAGE BODY", test.getObjectName().toUpperCase(), + expectation.getCallerLine(), 1); + } else { + openTest(test); + } + } + } + + private String getHtml(final String text) { + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + sb.append("\t\n"); + sb.append("\t\t\n"); + sb.append("\t\n"); + sb.append("\t\n"); + sb.append("\t\t"); + sb.append(getLinkedAndFormattedText(text)); + sb.append('\n'); + sb.append("\t\n"); + sb.append("\n"); + return sb.toString(); + } + + private void openLink(final String link) { + final String[] parts = link.split("/"); + final String type = parts[0]; + final String ownerName = parts[1]; + final String objectName = parts[2]; + int line = Integer.parseInt(parts[3]); + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(currentRun.getConnectionName())); + final String objectType = "UNKNOWN".equals(type) ? dao.getObjectType(ownerName, objectName) : type; + if (parts.length == 5) { + final String procedureName = parts[4]; + final String source = dao.getSource(ownerName, objectType, objectName).trim(); + final UtplsqlParser parser = new UtplsqlParser(source); + line = parser.getLineOf(procedureName); + } + openEditor(ownerName, objectType, objectName.toUpperCase(), line, 1); + } + + private void openEditor(final String owner, final String type, final String name, final int line, final int col) { + DefaultDrillLink drillLink = new DefaultDrillLink(); + drillLink.setConnName(currentRun.getConnectionName()); + // argument order is based on SQLDEV:LINK that can be used in SQL query result tables (editors, reports) + drillLink.setArgs(new String[] { owner, type, name, String.valueOf(line), String.valueOf(col), "OpenEditor", + "oracle.dbtools.raptor.controls.grid.DefaultDrillLink" }); + drillLink.performDrill(); + } + + private void syncDetailTab() { + if (syncDetailTabCheckBoxMenuItem.isSelected()) { + final int rowIndex = testOverviewTable.getSelectedRow(); + if (rowIndex != -1) { + final int row = testOverviewTable.convertRowIndexToModel(rowIndex); + final Test test = testOverviewTableModel.getTest(row); + int tabIndex = 0; + if (test != null && test.getCounter() != null) { + if (test.getCounter().getFailure() != null && test.getCounter().getFailure() > 0) { + tabIndex = 1; + } else if (test.getCounter().getError() != null && test.getCounter().getError() > 0) { + tabIndex = 2; + } else if (test.getCounter().getWarning() != null && test.getCounter().getWarning() > 0) { + tabIndex = 3; + } else if (test.getServerOutput() != null && test.getServerOutput().length() > 0) { + tabIndex = 4; + } + } + testDetailTabbedPane.setSelectedIndex(tabIndex); + } + } + } + + private PreferenceModel getPreferenceModel() { + PreferenceModel preferences = null; + try { + preferences = PreferenceModel.getInstance(Preferences.getPreferences()); + } catch (NoClassDefFoundError e) { + // running outside of SQL Developer + preferences = PreferenceModel.getInstance(null); + } + return preferences; + } + + private void applyPreferences() { + final PreferenceModel preferences = getPreferenceModel(); + applyShowNumberOfRunsInHistory(preferences.getNumberOfRunsInHistory()); + showDisabledCounterCheckBoxMenuItem.setSelected(preferences.isShowDisabledCounter()); + applyShowDisabledCounter(); + fixCheckBoxMenuItem(showDisabledCounterCheckBoxMenuItem); + showWarningsCounterCheckBoxMenuItem.setSelected(preferences.isShowWarningsCounter()); + applyShowWarningsCounter(); + fixCheckBoxMenuItem(showWarningsCounterCheckBoxMenuItem); + showInfoCounterCheckBoxMenuItem.setSelected(preferences.isShowInfoCounter()); + applyShowInfoCounter(); + fixCheckBoxMenuItem(showInfoCounterCheckBoxMenuItem); + showTestDescriptionCheckBoxMenuItem.setSelected(preferences.isShowTestDescription()); + applyShowTestDescription(); + fixCheckBoxMenuItem(showTestDescriptionCheckBoxMenuItem); + showWarningIndicatorCheckBoxMenuItem.setSelected(preferences.isShowWarningIndicator()); + applyShowWarningIndicator(showWarningIndicatorCheckBoxMenuItem.isSelected()); + fixCheckBoxMenuItem(showWarningIndicatorCheckBoxMenuItem); + showInfoIndicatorCheckBoxMenuItem.setSelected(preferences.isShowInfoIndicator()); + applyShowInfoIndicator(showInfoIndicatorCheckBoxMenuItem.isSelected()); + showSuccessfulTestsCheckBoxMenuItem.setSelected(preferences.isShowSuccessfulTests()); + fixCheckBoxMenuItem(showSuccessfulTestsCheckBoxMenuItem); + showDisabledTestsCheckBoxMenuItem.setSelected(preferences.isShowDisabledTests()); + fixCheckBoxMenuItem(showDisabledTestsCheckBoxMenuItem); + applyFilter(showSuccessfulTestsCheckBoxMenuItem.isSelected(), showDisabledTestsCheckBoxMenuItem.isSelected()); + fixCheckBoxMenuItem(showInfoIndicatorCheckBoxMenuItem); + syncDetailTabCheckBoxMenuItem.setSelected(preferences.isSyncDetailTab()); + fixCheckBoxMenuItem(syncDetailTabCheckBoxMenuItem); + useSmartTimes = preferences.isUseSmartTimes(); + } + + public void setModel(final Run run) { + runs.put(run.getReporterId(), run); + refreshRunsComboBox(); + setCurrentRun(run); + } + + private void setCurrentRun(final Run run) { + if (run != currentRun) { + currentRun = run; + testOverviewTableModel.setModel(run.getTests(), showTestDescriptionCheckBoxMenuItem.isSelected(), + useSmartTimes); + final String header = testOverviewTableModel.getTimeColumnName(); + final TableColumn timeColumn = testOverviewTable.getColumnModel().getColumn(4); + if (!timeColumn.getHeaderValue().equals(header)) { + timeColumn.setHeaderValue(header); + testOverviewTable.getTableHeader().repaint(); + } + resetDerived(); + final ComboBoxItem item = new ComboBoxItem<>(currentRun.getReporterId(), + currentRun.getName()); + runComboBox.setSelectedItem(item); + elapsedTimeTimer.start(); + } + } + + public synchronized void update(final String reporterId) { + setCurrentRun(runs.get(reporterId)); + final int row = currentRun.getCurrentTestNumber() - 1; + final CharSequence header = testOverviewTableModel.getTestIdColumnName(); + final TableColumn idColumn = testOverviewTable.getColumnModel().getColumn(3); + if (!idColumn.getHeaderValue().equals(header)) { + idColumn.setHeaderValue(header); + testOverviewTable.getTableHeader().repaint(); + } + if (row < 0) { + testOverviewTableModel.fireTableDataChanged(); + } else { + if (testOverviewTableModel.getRowCount() > row) { + final Rectangle positionOfCurrentTest = testOverviewTable + .getCellRect(testOverviewTable.convertRowIndexToView(row), 0, true); + testOverviewTable.scrollRectToVisible(positionOfCurrentTest); + testOverviewTableModel.fireTableRowsUpdated(row, row); + SystemTools.sleep(5); + if (!showSuccessfulTestsCheckBoxMenuItem.isSelected() + || !showDisabledTestsCheckBoxMenuItem.isSelected()) { + applyFilter(showSuccessfulTestsCheckBoxMenuItem.isSelected(), + showDisabledTestsCheckBoxMenuItem.isSelected()); + } + testOverviewTable.scrollRectToVisible(positionOfCurrentTest); + } + } + statusLabel.setText(currentRun.getStatus()); + testCounterValueLabel.setText(currentRun.getTotalNumberOfCompletedTests() + + (currentRun.getTotalNumberOfTests() >= 0 ? "/" + currentRun.getTotalNumberOfTests() : "")); + errorCounterValueLabel.setText(String.valueOf(currentRun.getCounter().getError())); + failureCounterValueLabel.setText(String.valueOf(currentRun.getCounter().getFailure())); + disabledCounterValueLabel.setText(String.valueOf(currentRun.getCounter().getDisabled())); + warningsCounterValueLabel.setText(String.valueOf(currentRun.getCounter().getWarning())); + infoCounterValueLabel.setText(String.valueOf(currentRun.getInfoCount())); + if (currentRun.getTotalNumberOfTests() == 0) { + progressBar.setValue(100); + } else { + progressBar + .setValue(100 * currentRun.getTotalNumberOfCompletedTests() / currentRun.getTotalNumberOfTests()); + } + if (currentRun.getCounter().getError() > 0 || (currentRun.getCounter().getFailure() > 0)) { + progressBar.setForeground(RED); + } else { + progressBar.setForeground(GREEN); + } + } + + private ArrayList getPathListFromSelectedTests() { + final ArrayList pathList = new ArrayList<>(); + for (final int rowIndex : testOverviewTable.getSelectedRows()) { + final int row = testOverviewTable.convertRowIndexToModel(rowIndex); + final Test test = testOverviewTableModel.getTest(row); + final String path = test.getOwnerName() + "." + test.getObjectName() + "." + test.getProcedureName(); + pathList.add(path); + } + return pathList; + } + + private boolean isWindowsLookAndFeel() { + LookAndFeel laf = UIManager.getLookAndFeel(); + final String lafName = laf != null ? laf.getName() : null; + return "Windows".equals(lafName); + } + + private boolean isMacLookAndFeel() { + LookAndFeel laf = UIManager.getLookAndFeel(); + final String lafName = laf != null ? laf.getName() : null; + return "Mac OS X".equals(lafName); + } + + private void fixCheckBoxMenuItem(final JCheckBoxMenuItem item) { + if (isWindowsLookAndFeel()) { + if (item.isSelected()) { + item.setIcon(UtplsqlResources.getIcon("CHECKMARK_ICON")); + } else { + item.setIcon(null); + } + } + } + + private void comboBoxAction() { + if (currentRun != null) { + @SuppressWarnings("unchecked") + final ComboBoxItem comboBoxItem = (ComboBoxItem) runComboBox + .getSelectedItem(); + if (currentRun.getReporterId() != null && !currentRun.getReporterId().equals(comboBoxItem.getKey())) { + update(comboBoxItem.getKey()); + testDetailTabbedPane.setSelectedIndex(0); + } + } + } + + private String getLinkedAndFormattedText(final String text) { + if (text == null) { + return ""; + } + // Patterns (primarily Asserts, Errors, ServerOutput): + // at "OWNER.PACKAGE.PROCEDURE", line 42 + // at "OWNER.PROCEDURE", line 42 + // at "OWNER.PACKAGE", line 42 + // at package "OWNER.PACKAGE", line 42 + final Pattern p1 = Pattern.compile("\\s+(package\\s+)?("(\\S+?)\\.(\\S+?)(?:\\.(\\S+?))?",\\s+line\\s+([0-9]+))"); + String localText = HtmlUtils.htmlEscape(text); + Matcher m = p1.matcher(localText); + while (m.find()) { + final String link = "" + m.group(2) + ""; + final int start = m.start(2); + final int end = m.end(2); + localText = localText.substring(0, start) + link + localText.substring(end); + m = p1.matcher(localText); + } + // Patterns (primarily Warnings, without line reference, calculate when opening link): + // owner.package.procedure + final Pattern p2 = Pattern.compile("^\\s{2}((\\S+?)\\.(\\S+?)\\.(\\S+?))$", Pattern.MULTILINE); + m = p2.matcher(localText); + while (m.find()) { + final String link = "  " + m.group(1) + ""; + final int start = m.start(0); + final int end = m.end(0); + localText = localText.substring(0, start) + link + localText.substring(end); + m = p2.matcher(localText); + } + // Patterns (Title for warning/info on suite level) + // from suite a.junit_utplsql_test1_pkg: + final Pattern p3 = Pattern.compile("^For suite ([^:]+):$", Pattern.MULTILINE); + m = p3.matcher(localText); + while (m.find()) { + final String title = "For suite \"" + m.group(1) + "\""; + final int start = m.start(0); + final int end = m.end(0); + localText = localText.substring(0, start) + title + localText.substring(end); + m = p3.matcher(localText); + } + StringBuilder sb = new StringBuilder(); + for (final String p : localText.split("\n")) { + sb.append("

"); + sb.append(p); + sb.append("

\n"); + } + return sb.toString(); + } + + private JPanel makeLabelledCounterComponent(final JLabel label, final JComponent comp) { + final JPanel groupPanel = new JPanel(); + groupPanel.setLayout(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + // label + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(5, 10, 5, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + c.weighty = 0; + groupPanel.add(label, c); + // component + c.gridx = 1; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(5, 5, 5, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.weighty = 0; + groupPanel.add(comp, c); + final Dimension dim = new Dimension(134, 24); + groupPanel.setMinimumSize(dim); + groupPanel.setPreferredSize(dim); + return groupPanel; + } + + private void initializeGUI() { + // Base panel containing all components + basePanel = new JPanel(); + basePanel.setLayout(new GridBagLayout()); + final GridBagConstraints c = new GridBagConstraints(); + + // Toolbar + final GradientToolbar toolbar = new GradientToolbar(); + toolbar.setFloatable(false); + final EmptyBorder buttonBorder = new EmptyBorder(new Insets(2, 4, 2, 4)); // insets: top, left, bottom, right + final ToolbarButton refreshButton = new ToolbarButton(UtplsqlResources.getIcon("REFRESH_ICON")); + refreshButton.setToolTipText(UtplsqlResources.getString("RUNNER_REFRESH_TOOLTIP")); + refreshButton.setBorder(buttonBorder); + refreshButton.addActionListener(event -> { + resetDerived(); + testDetailTabbedPane.setSelectedIndex(0); + testOverviewTableModel.fireTableDataChanged(); + }); + toolbar.add(refreshButton); + final ToolbarButton rerunButton = new ToolbarButton(UtplsqlResources.getIcon("RUN_ICON")); + rerunButton.setToolTipText(UtplsqlResources.getString("RUNNER_RERUN_TOOLTIP")); + rerunButton.setBorder(buttonBorder); + rerunButton.addActionListener(event -> { + final UtplsqlRunner runner = new UtplsqlRunner(currentRun.getPathList(), currentRun.getConnectionName()); + runner.runTestAsync(); + }); + toolbar.add(rerunButton); + final ToolbarButton rerunWorksheetButton = new ToolbarButton(UtplsqlResources.getIcon("RUN_WORKSHEET_ICON")); + rerunWorksheetButton.setToolTipText(UtplsqlResources.getString("RUNNER_RERUN_WORKSHEET_TOOLTIP")); + rerunWorksheetButton.setBorder(buttonBorder); + rerunWorksheetButton.addActionListener(event -> { + final UtplsqlWorksheetRunner worksheet = new UtplsqlWorksheetRunner(currentRun.getPathList(), + currentRun.getConnectionName()); + worksheet.runTestAsync(); + }); + toolbar.add(rerunWorksheetButton); + toolbar.add(Box.createHorizontalGlue()); + runComboBoxModel = new DefaultComboBoxModel<>(); + runComboBox = new JComboBox<>(runComboBoxModel); + runComboBox.setEditable(false); + final Dimension comboBoxDim = new Dimension(500, 50); + runComboBox.setMaximumSize(comboBoxDim); + runComboBox.addActionListener(event -> comboBoxAction()); + toolbar.add(runComboBox); + final ToolbarButton clearButton = new ToolbarButton(UtplsqlResources.getIcon("CLEAR_ICON")); + clearButton.setToolTipText(UtplsqlResources.getString("RUNNER_CLEAR_BUTTON")); + clearButton.setBorder(buttonBorder); + clearButton.addActionListener(event -> { + final Run run = currentRun; + runs.clear(); + currentRun = null; + setModel(run); + update(run.getReporterId()); + }); + toolbar.add(clearButton); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + c.gridheight = 1; + c.insets = new Insets(0, 0, 0, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.NORTH; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.weighty = 0; + basePanel.add(toolbar, c); + + // Status line + statusLabel = new JLabel(); + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(10, 10, 10, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.weighty = 0; + basePanel.add(statusLabel, c); + JLabel elapsedTimeLabel = new JLabel(); + elapsedTimeLabel.setPreferredSize(new Dimension(60, 0)); + c.gridx = 1; + c.gridy = 1; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(10, 10, 10, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + c.weighty = 0; + basePanel.add(elapsedTimeLabel, c); + elapsedTimeTimer = new Timer(100, event -> { + if (currentRun != null && currentRun.getStart() != null) { + final SmartTime time = new SmartTime(); + time.setSmart(useSmartTimes); + if (currentRun.getExecutionTime() != null) { + time.setSeconds(currentRun.getExecutionTime()); + elapsedTimeTimer.stop(); + } else { + final Double now = Double.valueOf(System.currentTimeMillis()); + time.setSeconds(Double.valueOf(now - currentRun.getStart()) / 1000); + } + elapsedTimeLabel.setText(time.toString() + (!useSmartTimes ? " s" : "")); + } else { + elapsedTimeLabel.setText(null); + } + }); + + // Counters + // - Test counter + final JPanel counterPanel = new JPanel(); + counterPanel.setLayout(new WrapLayout(FlowLayout.LEFT, 0, 0)); + final JLabel testCounterLabel = new JLabel(UtplsqlResources.getString("RUNNER_TESTS_LABEL") + ":", JLabel.LEADING); + testCounterValueLabel = new JLabel(); + counterPanel.add(makeLabelledCounterComponent(testCounterLabel, testCounterValueLabel)); + // - Failure counter + final JLabel failureCounterLabel = new JLabel(UtplsqlResources.getString("RUNNER_FAILURES_LABEL") + ":", + UtplsqlResources.getIcon("FAILURE_ICON"), JLabel.LEADING); + failureCounterValueLabel = new JLabel(); + counterPanel.add(makeLabelledCounterComponent(failureCounterLabel, failureCounterValueLabel)); + // - Error counter + final JLabel errorCounterLabel = new JLabel(UtplsqlResources.getString("RUNNER_ERRORS_LABEL") + ":", + UtplsqlResources.getIcon("ERROR_ICON"), JLabel.LEADING); + errorCounterValueLabel = new JLabel(); + counterPanel.add(makeLabelledCounterComponent(errorCounterLabel, errorCounterValueLabel)); + // - Disabled counter + final JLabel disabledCounterLabel = new JLabel(UtplsqlResources.getString("RUNNER_DISABLED_LABEL") + ":", + UtplsqlResources.getIcon("DISABLED_ICON"), JLabel.LEADING); + disabledCounterValueLabel = new JLabel(); + counterPanel.add(makeLabelledCounterComponent(disabledCounterLabel, disabledCounterValueLabel)); + // - Warnings counter + final JLabel warningsCounterLabel = new JLabel(UtplsqlResources.getString("RUNNER_WARNINGS_LABEL") + ":", + UtplsqlResources.getIcon("WARNING_ICON"), JLabel.LEADING); + warningsCounterValueLabel = new JLabel(); + counterPanel.add(makeLabelledCounterComponent(warningsCounterLabel, warningsCounterValueLabel)); + // - Info counter + final JLabel infoCounterLabel = new JLabel(UtplsqlResources.getString("RUNNER_INFO_LABEL") + ":", + UtplsqlResources.getIcon("INFO_ICON"), JLabel.LEADING); + infoCounterValueLabel = new JLabel(); + counterPanel.add(makeLabelledCounterComponent(infoCounterLabel, infoCounterValueLabel)); + // - add everything to basePanel + c.gridx = 0; + c.gridy = 2; + c.gridwidth = 2; + c.gridheight = 1; + c.insets = new Insets(5, 0, 5, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.weighty = 0; + basePanel.add(counterPanel, c); + + // Context menu for counters panel + final JPopupMenu countersPopupMenu = new JPopupMenu(); + showDisabledCounterCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_DISABLED_COUNTER_LABEL").replace("?", ""), true); + showDisabledCounterCheckBoxMenuItem.addActionListener(event -> { + applyShowDisabledCounter(); + fixCheckBoxMenuItem(showDisabledCounterCheckBoxMenuItem); + }); + countersPopupMenu.add(showDisabledCounterCheckBoxMenuItem); + showWarningsCounterCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_WARNINGS_COUNTER_LABEL").replace("?", ""), true); + showWarningsCounterCheckBoxMenuItem.addActionListener(event -> { + applyShowWarningsCounter(); + fixCheckBoxMenuItem(showWarningsCounterCheckBoxMenuItem); + }); + countersPopupMenu.add(showWarningsCounterCheckBoxMenuItem); + showInfoCounterCheckBoxMenuItem = new JCheckBoxMenuItem( UtplsqlResources.getString("PREF_SHOW_INFO_COUNTER_LABEL").replace("?", ""), true); + showInfoCounterCheckBoxMenuItem.addActionListener(event -> { + applyShowInfoCounter(); + fixCheckBoxMenuItem(showInfoCounterCheckBoxMenuItem); + }); + countersPopupMenu.add(showInfoCounterCheckBoxMenuItem); + counterPanel.setComponentPopupMenu(countersPopupMenu); + + // Progress bar + progressBar = new JProgressBar(); + final Dimension progressBarDim = new Dimension(10, 20); + progressBar.setPreferredSize(progressBarDim); + progressBar.setMinimumSize(progressBarDim); + progressBar.setStringPainted(false); + progressBar.setForeground(GREEN); + progressBar.setUI(new BasicProgressBarUI()); + c.gridx = 0; + c.gridy = 3; + c.gridwidth = 2; + c.gridheight = 1; + c.insets = new Insets(10, 10, 10, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.weighty = 0; + basePanel.add(progressBar, c); + + // Test overview + testOverviewTableModel = new TestOverviewTableModel(); + testOverviewTable = new JTable(testOverviewTableModel); + testOverviewTable.getTableHeader().setReorderingAllowed(false); + testOverviewTable.setAutoCreateRowSorter(true); + testOverviewTable.setRowHeight(OVERVIEW_TABLE_ROW_HEIGHT); + testOverviewTable.getTableHeader().setPreferredSize( + new Dimension(testOverviewTable.getTableHeader().getPreferredSize().width, OVERVIEW_TABLE_ROW_HEIGHT)); + testOverviewTable.getSelectionModel().addListSelectionListener(event -> { + final int rowIndex = testOverviewTable.getSelectedRow(); + if (rowIndex != -1) { + final int row = testOverviewTable.convertRowIndexToModel(rowIndex); + final Test test = testOverviewTableModel.getTest(row); + testOwnerTextField.setText(test.getOwnerName()); + testPackageTextField.setText(test.getObjectName()); + testProcedureTextField.setText(test.getProcedureName()); + testDescriptionTextArea.setText(test.getDescription() != null ? test.getDescription().trim() : null); + testIdTextArea.setText(test.getId()); + testStartTextField.setText(StringTools.formatDateTime(test.getStartTime())); + failuresTableModel.setModel(test.getFailedExpectations()); + failuresTableModel.fireTableDataChanged(); + testFailureMessageTextPane.setText(null); + if (test.getFailedExpectations() != null && !test.getFailedExpectations().isEmpty()) { + failuresTable.setRowSelectionInterval(0, 0); + } + testErrorStackTextPane + .setText(getHtml(test.getErrorStack() != null ? test.getErrorStack().trim() : null)); + testWarningsTextPane.setText(getHtml(test.getWarnings() != null ? test.getWarnings().trim() : null)); + testServerOutputTextPane + .setText(getHtml(test.getServerOutput() != null ? test.getServerOutput().trim() : null)); + syncDetailTab(); + testOverviewRunMenuItem.setEnabled(true); + testOverviewRunWorksheetMenuItem.setEnabled(true); + } + }); + testOverviewTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(final MouseEvent e) { + if (e.getClickCount() == 2) { + if (failuresTable.getSelectedRowCount() == 1) { + openSelectedFailure(); + } else { + openSelectedTest(); + } + } + } + }); + RepaintManager.currentManager(testOverviewTable).setDoubleBufferingEnabled(true); + final TestTableHeaderRenderer testTableHeaderRenderer = new TestTableHeaderRenderer(); + final TableColumn overviewTableStatus = testOverviewTable.getColumnModel().getColumn(0); + overviewTableStatus.setMinWidth(INDICATOR_WIDTH); + overviewTableStatus.setPreferredWidth(INDICATOR_WIDTH); + overviewTableStatus.setMaxWidth(INDICATOR_WIDTH); + overviewTableStatus.setHeaderRenderer(testTableHeaderRenderer); + final TableColumn overviewTableWarning = testOverviewTable.getColumnModel().getColumn(1); + overviewTableWarning.setMinWidth(INDICATOR_WIDTH); + overviewTableWarning.setPreferredWidth(INDICATOR_WIDTH); + overviewTableWarning.setMaxWidth(INDICATOR_WIDTH); + overviewTableWarning.setHeaderRenderer(testTableHeaderRenderer); + final TableColumn overviewTableInfo = testOverviewTable.getColumnModel().getColumn(2); + overviewTableInfo.setMinWidth(INDICATOR_WIDTH); + overviewTableInfo.setPreferredWidth(INDICATOR_WIDTH); + overviewTableInfo.setMaxWidth(INDICATOR_WIDTH); + overviewTableInfo.setHeaderRenderer(testTableHeaderRenderer); + final TableColumn overviewTableId = testOverviewTable.getColumnModel().getColumn(3); + overviewTableId.setHeaderRenderer(testTableHeaderRenderer); + final TableColumn overviewTableTime = testOverviewTable.getColumnModel().getColumn(4); + overviewTableTime.setPreferredWidth(60); + overviewTableTime.setMaxWidth(100); + overviewTableTime.setHeaderRenderer(testTableHeaderRenderer); + overviewTableTime.setCellRenderer(new DefaultTableCellRenderer() { + private static final long serialVersionUID = 7720067427609773267L; + { + setHorizontalAlignment(JLabel.RIGHT); + } + + @Override + public Component getTableCellRendererComponent(final JTable table, final Object value, + final boolean isSelected, final boolean hasFocus, final int row, final int col) { + final SmartTime smartTime = new SmartTime(((Double) value), useSmartTimes); + return super.getTableCellRendererComponent(table, smartTime.toString(), isSelected, hasFocus, row, col); + } + }); + final JScrollPane testOverviewScrollPane = new JScrollPane(testOverviewTable); + + // Context menu for test overview + final JPopupMenu testOverviewPopupMenu = new JPopupMenu(); + testOverviewRunMenuItem = new JMenuItem(UtplsqlResources.getString("RUNNER_RUN_MENUITEM"), UtplsqlResources.getIcon("RUN_ICON")); + testOverviewRunMenuItem.addActionListener(event -> { + final UtplsqlRunner runner = new UtplsqlRunner(getPathListFromSelectedTests(), + currentRun.getConnectionName()); + runner.runTestAsync(); + }); + testOverviewPopupMenu.add(testOverviewRunMenuItem); + testOverviewRunWorksheetMenuItem = new JMenuItem(UtplsqlResources.getString("RUNNER_RUN_WORKSHEET_MENUITEM"), UtplsqlResources.getIcon("RUN_WORKSHEET_ICON")); + testOverviewRunWorksheetMenuItem.addActionListener(event -> { + final UtplsqlWorksheetRunner worksheet = new UtplsqlWorksheetRunner(this.getPathListFromSelectedTests(), + currentRun.getConnectionName()); + worksheet.runTestAsync(); + }); + testOverviewPopupMenu.add(testOverviewRunWorksheetMenuItem); + testOverviewPopupMenu.add(new JSeparator()); + showSuccessfulTestsCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_SUCCESSFUL_TESTS_LABEL").replace("?", ""), true); + showSuccessfulTestsCheckBoxMenuItem.addActionListener(event -> { + applyFilter(showSuccessfulTestsCheckBoxMenuItem.isSelected(), + showDisabledTestsCheckBoxMenuItem.isSelected()); + fixCheckBoxMenuItem(showSuccessfulTestsCheckBoxMenuItem); + }); + testOverviewPopupMenu.add(showSuccessfulTestsCheckBoxMenuItem); + showDisabledTestsCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_DISABLED_TESTS_LABEL").replace("?", ""), true); + showDisabledTestsCheckBoxMenuItem.addActionListener(event -> { + applyFilter(showSuccessfulTestsCheckBoxMenuItem.isSelected(), + showDisabledTestsCheckBoxMenuItem.isSelected()); + fixCheckBoxMenuItem(showDisabledTestsCheckBoxMenuItem); + }); + testOverviewPopupMenu.add(showDisabledTestsCheckBoxMenuItem); + testOverviewPopupMenu.add(new JSeparator()); + showTestDescriptionCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_TEST_DESCRIPTION_LABEL").replace("?", ""), true); + showTestDescriptionCheckBoxMenuItem.addActionListener(event -> { + applyShowTestDescription(); + fixCheckBoxMenuItem(showTestDescriptionCheckBoxMenuItem); + }); + testOverviewPopupMenu.add(showTestDescriptionCheckBoxMenuItem); + showWarningIndicatorCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_WARNING_INDICATOR_LABEL").replace("?", ""), true); + showWarningIndicatorCheckBoxMenuItem.addActionListener(event -> { + applyShowWarningIndicator(showWarningIndicatorCheckBoxMenuItem.isSelected()); + fixCheckBoxMenuItem(showWarningIndicatorCheckBoxMenuItem); + }); + testOverviewPopupMenu.add(showWarningIndicatorCheckBoxMenuItem); + showInfoIndicatorCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_INFO_INDICATOR_LABEL").replace("?", ""), true); + showInfoIndicatorCheckBoxMenuItem.addActionListener(event -> { + applyShowInfoIndicator(showInfoIndicatorCheckBoxMenuItem.isSelected()); + fixCheckBoxMenuItem(showInfoIndicatorCheckBoxMenuItem); + }); + testOverviewPopupMenu.add(showInfoIndicatorCheckBoxMenuItem); + syncDetailTabCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SYNC_DETAIL_TAB_LABEL").replace("?", ""), true); + syncDetailTabCheckBoxMenuItem.addActionListener(event -> { + syncDetailTab(); + fixCheckBoxMenuItem(syncDetailTabCheckBoxMenuItem); + }); + testOverviewPopupMenu.add(syncDetailTabCheckBoxMenuItem); + testOverviewTable.setComponentPopupMenu(testOverviewPopupMenu); + testOverviewTable.getTableHeader().setComponentPopupMenu(testOverviewPopupMenu); + + // Test tabbed pane (Test Properties) + final ScrollablePanel testInfoPanel = new ScrollablePanel(); + testInfoPanel.setLayout(new GridBagLayout()); + // - Owner + final JLabel testOwnerLabel = new JLabel(UtplsqlResources.getString("RUNNER_OWNER_LABEL")); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(10, 10, 0, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + c.weighty = 0; + testInfoPanel.add(testOwnerLabel, c); + testOwnerTextField = new RunnerTextField(); + testOwnerTextField.setEditable(false); + c.gridx = 1; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(10, 5, 0, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.weighty = 0; + testInfoPanel.add(testOwnerTextField, c); + // - Package + final JLabel testPackageLabel = new JLabel(UtplsqlResources.getString("RUNNER_PACKAGE_LABEL")); + c.gridx = 0; + c.gridy = 1; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(5, 10, 0, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + c.weighty = 0; + testInfoPanel.add(testPackageLabel, c); + testPackageTextField = new RunnerTextField(); + testPackageTextField.setEditable(false); + c.gridx = 1; + c.gridy = 1; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(5, 5, 0, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.weighty = 0; + testInfoPanel.add(testPackageTextField, c); + // - Procedure + final JLabel testProcedureLabel = new JLabel(UtplsqlResources.getString("RUNNER_PROCEDURE_LABEL")); + c.gridx = 0; + c.gridy = 2; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(5, 10, 0, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + c.weighty = 0; + testInfoPanel.add(testProcedureLabel, c); + testProcedureTextField = new RunnerTextField(); + testProcedureTextField.setEditable(false); + c.gridx = 1; + c.gridy = 2; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(5, 5, 0, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.weighty = 0; + testInfoPanel.add(testProcedureTextField, c); + // - Description + final JLabel testDescriptionLabel = new JLabel(UtplsqlResources.getString("RUNNER_DESCRIPTION_LABEL")); + testDescriptionLabel.setBorder(BorderFactory.createEmptyBorder(isMacLookAndFeel() ? 5 : 3, 0, 0, 0)); + c.gridx = 0; + c.gridy = 3; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(5, 10, 0, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.NORTHWEST; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + c.weighty = 0; + testInfoPanel.add(testDescriptionLabel, c); + testDescriptionTextArea = new RunnerTextArea(); + testDescriptionTextArea.setEditable(false); + testDescriptionTextArea.setEnabled(true); + testDescriptionTextArea.setLineWrap(true); + testDescriptionTextArea.setWrapStyleWord(true); + c.gridx = 1; + c.gridy = 3; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(5, 5, 0, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.weighty = 0; + testInfoPanel.add(testDescriptionTextArea, c); + // - Suitepath (id) + final JLabel testIdLabel = new JLabel(UtplsqlResources.getString("RUNNER_TEST_ID_COLUMN")); + testIdLabel.setBorder(BorderFactory.createEmptyBorder(isMacLookAndFeel() ? 5 : 3, 0, 0, 0)); + c.gridx = 0; + c.gridy = 4; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(5, 10, 0, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.NORTHWEST; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + c.weighty = 0; + testInfoPanel.add(testIdLabel, c); + testIdTextArea = new RunnerTextArea(); + testIdTextArea.setEditable(false); + testIdTextArea.setEnabled(true); + testIdTextArea.setLineWrap(true); + testIdTextArea.setWrapStyleWord(false); + c.gridx = 1; + c.gridy = 4; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(5, 5, 0, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.weighty = 0; + testInfoPanel.add(testIdTextArea, c); + // - Start + final JLabel testStartLabel = new JLabel(UtplsqlResources.getString("RUNNER_START_LABEL")); + c.gridx = 0; + c.gridy = 5; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(5, 10, 10, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.NONE; + c.weightx = 0; + c.weighty = 0; + testInfoPanel.add(testStartLabel, c); + testStartTextField = new RunnerTextField(); + testStartTextField.setEditable(false); + c.gridx = 1; + c.gridy = 5; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(5, 5, 10, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.HORIZONTAL; + c.weightx = 1; + c.weighty = 0; + testInfoPanel.add(testStartTextField, c); + c.gridx = 0; + c.gridy = 6; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(0, 0, 0, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 0; + c.weighty = 1; + testInfoPanel.add(Box.createVerticalGlue(), c); + final JScrollPane testPropertiesScrollPane = new JScrollPane(testInfoPanel); + + // Failures tabbed pane (failed expectations) + // - failures table (number and description) + failuresTableModel = new FailuresTableModel(); + failuresTable = new JTable(failuresTableModel); + failuresTable.getTableHeader().setReorderingAllowed(false); + failuresTable.getSelectionModel().addListSelectionListener(event -> { + final int rowIndex = failuresTable.getSelectedRow(); + if (rowIndex != -1) { + final int row = failuresTable.convertRowIndexToModel(rowIndex); + final Expectation expectation = failuresTableModel.getExpectation(row); + final String html = getHtml(expectation.getFailureText()); + testFailureMessageTextPane.setText(html); + } + }); + failuresTable.addMouseListener(new MouseAdapter() { + @Override + public void mouseClicked(final MouseEvent e) { + if (e.getClickCount() == 2 && failuresTable.getSelectedRowCount() == 1) { + openSelectedFailure(); + } + } + }); + final FailuresTableHeaderRenderer failuresTableHeaderRenderer = new FailuresTableHeaderRenderer(); + final TableColumn failuresTableNumber = failuresTable.getColumnModel().getColumn(0); + failuresTableNumber.setHeaderRenderer(failuresTableHeaderRenderer); + failuresTableNumber.setPreferredWidth(30); + failuresTableNumber.setMaxWidth(30); + final TableColumn failuresDescription = failuresTable.getColumnModel().getColumn(1); + failuresDescription.setHeaderRenderer(failuresTableHeaderRenderer); + final JScrollPane failuresTableScrollPane = new JScrollPane(failuresTable); + // - failures details + testFailureMessageTextPane = new RunnerTextPane(); + testFailureMessageTextPane.setEditable(false); + testFailureMessageTextPane.setEnabled(true); + testFailureMessageTextPane.setContentType("text/html"); + testFailureMessageTextPane.setMinimumSize(TEXTPANE_DIM); + testFailureMessageTextPane.setPreferredSize(TEXTPANE_DIM); + testFailureMessageTextPane.addHyperlinkListener(event -> { + if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + final String link = event.getDescription(); + openLink(link); + } + }); + final JScrollPane testFailureMessageScrollPane = new JScrollPane(testFailureMessageTextPane); + c.gridx = 1; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(10, 5, 0, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 6; + + // - split pane + final JSplitPane failuresSplitPane = new JSplitPane(SwingConstants.HORIZONTAL, failuresTableScrollPane, + testFailureMessageScrollPane); + failuresSplitPane.setResizeWeight(0.2); + + // Errors tabbed pane (Error Stack) + final JPanel testErrorStackPanel = new JPanel(); + testErrorStackPanel.setLayout(new GridBagLayout()); + testErrorStackTextPane = new RunnerTextPane(); + testErrorStackTextPane.setEditable(false); + testErrorStackTextPane.setEnabled(true); + testErrorStackTextPane.setContentType("text/html"); + testErrorStackTextPane.setMinimumSize(TEXTPANE_DIM); + testErrorStackTextPane.setPreferredSize(TEXTPANE_DIM); + testErrorStackTextPane.addHyperlinkListener(event -> { + if (event.getEventType() == HyperlinkEvent.EventType.ACTIVATED) { + final String link = event.getDescription(); + openLink(link); + } + }); + final JScrollPane testErrorStackScrollPane = new JScrollPane(testErrorStackTextPane); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(0, 0, 0, 0); + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + testErrorStackPanel.add(testErrorStackScrollPane, c); + + // Warnings tabbed pane + final JPanel testWarningsPanel = new JPanel(); + testWarningsPanel.setLayout(new GridBagLayout()); + testWarningsTextPane = new RunnerTextPane(); + testWarningsTextPane.setEditable(false); + testWarningsTextPane.setEnabled(true); + testWarningsTextPane.setContentType("text/html"); + testWarningsTextPane.setMinimumSize(TEXTPANE_DIM); + testWarningsTextPane.setPreferredSize(TEXTPANE_DIM); + testWarningsTextPane.addHyperlinkListener(event -> openLink(event.getDescription())); + final JScrollPane testWarningsScrollPane = new JScrollPane(testWarningsTextPane); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(0, 0, 0, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + testWarningsPanel.add(testWarningsScrollPane, c); + + // Info tabbed pane (Server Output) + final JPanel testServerOutputPanel = new JPanel(); + testServerOutputPanel.setLayout(new GridBagLayout()); + testServerOutputTextPane = new RunnerTextPane(); + testServerOutputTextPane.setEditable(false); + testServerOutputTextPane.setEnabled(true); + testServerOutputTextPane.setContentType("text/html"); + testServerOutputTextPane.setMinimumSize(TEXTPANE_DIM); + testServerOutputTextPane.setPreferredSize(TEXTPANE_DIM); + testServerOutputTextPane.addHyperlinkListener(event -> openLink(event.getDescription())); + final JScrollPane testServerOutputScrollPane = new JScrollPane(testServerOutputTextPane); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 1; + c.gridheight = 1; + c.insets = new Insets(0, 0, 0, 0); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + testServerOutputPanel.add(testServerOutputScrollPane, c); + + // split pane with all tabs + testDetailTabbedPane = new JTabbedPane(); + testDetailTabbedPane.add(UtplsqlResources.getString("RUNNER_TEST_TAB_LABEL"), testPropertiesScrollPane); + testDetailTabbedPane.add(UtplsqlResources.getString("RUNNER_FAILURES_TAB_LABEL"), failuresSplitPane); + testDetailTabbedPane.add(UtplsqlResources.getString("RUNNER_ERRORS_TAB_LABEL"), testErrorStackPanel); + testDetailTabbedPane.add(UtplsqlResources.getString("RUNNER_WARNINGS_TAB_LABEL"), testWarningsPanel); + testDetailTabbedPane.add(UtplsqlResources.getString("RUNNER_INFO_TAB_LABEL"), testServerOutputPanel); + final JSplitPane horizontalSplitPane = new JSplitPane(SwingConstants.HORIZONTAL, testOverviewScrollPane, + testDetailTabbedPane); + horizontalSplitPane.setResizeWeight(0.5); + c.gridx = 0; + c.gridy = 4; + c.gridwidth = 2; + c.gridheight = 1; + c.insets = new Insets(10, 10, 10, 10); // top, left, bottom, right + c.anchor = GridBagConstraints.WEST; + c.fill = GridBagConstraints.BOTH; + c.weightx = 1; + c.weighty = 1; + basePanel.add(horizontalSplitPane, c); + if (isMacLookAndFeel()) { + final CompoundBorder border = BorderFactory.createCompoundBorder( + BorderFactory.createEmptyBorder(3, 3, 3, 3), + BorderFactory.createCompoundBorder(BorderFactory.createLineBorder(new Color(219, 219, 219)), + BorderFactory.createEmptyBorder(1, 1, 1, 1))); + testDescriptionTextArea.setBorder(border); + testIdTextArea.setBorder(border); + } else { + final Border referenceBorder = testOwnerTextField.getBorder(); + testDescriptionTextArea.setBorder(referenceBorder); + testIdTextArea.setBorder(referenceBorder); + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerPanel.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerPanel.xtend deleted file mode 100644 index 37a4f699..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerPanel.xtend +++ /dev/null @@ -1,1330 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.ui.runner - -import java.awt.Color -import java.awt.Component -import java.awt.Dimension -import java.awt.FlowLayout -import java.awt.GridBagConstraints -import java.awt.GridBagLayout -import java.awt.Insets -import java.awt.event.ActionEvent -import java.awt.event.ActionListener -import java.awt.event.MouseEvent -import java.awt.event.MouseListener -import java.util.ArrayList -import java.util.regex.Pattern -import javax.swing.BorderFactory -import javax.swing.Box -import javax.swing.DefaultComboBoxModel -import javax.swing.JCheckBoxMenuItem -import javax.swing.JComboBox -import javax.swing.JComponent -import javax.swing.JLabel -import javax.swing.JMenuItem -import javax.swing.JPanel -import javax.swing.JPopupMenu -import javax.swing.JProgressBar -import javax.swing.JScrollPane -import javax.swing.JSeparator -import javax.swing.JSplitPane -import javax.swing.JTabbedPane -import javax.swing.JTable -import javax.swing.RepaintManager -import javax.swing.RowFilter -import javax.swing.SwingConstants -import javax.swing.Timer -import javax.swing.UIManager -import javax.swing.border.EmptyBorder -import javax.swing.event.HyperlinkEvent -import javax.swing.event.HyperlinkListener -import javax.swing.event.ListSelectionEvent -import javax.swing.event.ListSelectionListener -import javax.swing.plaf.basic.BasicProgressBarUI -import javax.swing.table.DefaultTableCellRenderer -import javax.swing.table.TableRowSorter -import oracle.dbtools.raptor.controls.grid.DefaultDrillLink -import oracle.dbtools.raptor.utils.Connections -import oracle.ide.config.Preferences -import oracle.javatools.ui.table.ToolbarButton -import org.springframework.web.util.HtmlUtils -import org.utplsql.sqldev.dal.UtplsqlDao -import org.utplsql.sqldev.model.LimitedLinkedHashMap -import org.utplsql.sqldev.model.preference.PreferenceModel -import org.utplsql.sqldev.model.runner.Run -import org.utplsql.sqldev.model.runner.Test -import org.utplsql.sqldev.parser.UtplsqlParser -import org.utplsql.sqldev.resources.UtplsqlResources -import org.utplsql.sqldev.runner.UtplsqlRunner -import org.utplsql.sqldev.runner.UtplsqlWorksheetRunner - -class RunnerPanel implements ActionListener, MouseListener, HyperlinkListener { - static val GREEN = new Color(0, 153, 0) - static val RED = new Color(153, 0, 0) - static val INDICATOR_WIDTH = 20 - static val OVERVIEW_TABLE_ROW_HEIGHT = 20 - static val TEXTPANE_DIM = new Dimension(100, 100) - static var boolean useSmartTimes - LimitedLinkedHashMap runs = new LimitedLinkedHashMap(10) - Run currentRun - JPanel basePanel - ToolbarButton refreshButton - ToolbarButton rerunButton - ToolbarButton rerunWorksheetButton - DefaultComboBoxModel> runComboBoxModel - ToolbarButton clearButton - JComboBox> runComboBox - JLabel statusLabel - JLabel elapsedTimeLabel - Timer elapsedTimeTimer - JLabel testCounterValueLabel - JLabel errorCounterValueLabel - JLabel failureCounterValueLabel - JLabel disabledCounterValueLabel - JLabel warningsCounterValueLabel - JLabel infoCounterValueLabel - JCheckBoxMenuItem showDisabledCounterCheckBoxMenuItem - JCheckBoxMenuItem showWarningsCounterCheckBoxMenuItem - JCheckBoxMenuItem showInfoCounterCheckBoxMenuItem - JProgressBar progressBar; - TestOverviewTableModel testOverviewTableModel - JTable testOverviewTable - JMenuItem testOverviewRunMenuItem - JMenuItem testOverviewRunWorksheetMenuItem - JCheckBoxMenuItem showTestDescriptionCheckBoxMenuItem - JCheckBoxMenuItem showWarningIndicatorCheckBoxMenuItem - JCheckBoxMenuItem showInfoIndicatorCheckBoxMenuItem - JCheckBoxMenuItem showSuccessfulTestsCheckBoxMenuItem - JCheckBoxMenuItem showDisabledTestsCheckBoxMenuItem - JCheckBoxMenuItem syncDetailTabCheckBoxMenuItem - RunnerTextField testOwnerTextField - RunnerTextField testPackageTextField - RunnerTextField testProcedureTextField - RunnerTextArea testDescriptionTextArea - RunnerTextArea testIdTextArea - RunnerTextField testStartTextField - FailuresTableModel failuresTableModel - JTable failuresTable - RunnerTextPane testFailureMessageTextPane - RunnerTextPane testErrorStackTextPane - RunnerTextPane testWarningsTextPane - RunnerTextPane testServerOutputTextPane - JTabbedPane testDetailTabbedPane - - def Component getGUI() { - if (basePanel === null) { - initializeGUI() - } - if (!basePanel.showing) { - applyPreferences - } - return basePanel - } - - private def resetDerived() { - testOverviewTable.rowSorter.sortKeys = null - testOverviewRunMenuItem.enabled = false - testOverviewRunWorksheetMenuItem.enabled = false - testIdTextArea.text = null - testOwnerTextField.text = null - testPackageTextField.text = null - testProcedureTextField.text = null - testDescriptionTextArea.text = null - testStartTextField.text = null - failuresTableModel.model = null - failuresTableModel.fireTableDataChanged - testFailureMessageTextPane.text = null - testErrorStackTextPane.text = null - testWarningsTextPane.text = null - testServerOutputTextPane.text = null - } - - private def refreshRunsComboBox() { - if (runs.size > 0) { - runComboBox.removeActionListener(this) - runComboBoxModel.removeAllElements - for (var i = runs.size - 1 ; i >= 0; i--) { - val entry = runs.entrySet.get(i) - val item = new ComboBoxItem(entry.key, entry.value.name) - runComboBoxModel.addElement(item) - } - runComboBox.selectedIndex = 0 - runComboBox.addActionListener(this) - } - } - - private def applyShowNumberOfRunsInHistory(int maxRuns) { - if (maxRuns != runs.maxEntries) { - val newRuns = new LimitedLinkedHashMap(maxRuns) - for (entry : runs.entrySet) { - newRuns.put(entry.key, entry.value) - } - runs = newRuns - } - } - - private def applyShowDisabledCounter(boolean show) { - disabledCounterValueLabel.parent.visible = showDisabledCounterCheckBoxMenuItem.selected - } - - private def applyShowWarningsCounter(boolean show) { - warningsCounterValueLabel.parent.visible = showWarningsCounterCheckBoxMenuItem.selected - } - - private def applyShowInfoCounter(boolean show) { - infoCounterValueLabel.parent.visible = showInfoCounterCheckBoxMenuItem.selected - } - - private def applyShowTestDescription(boolean show) { - testOverviewTableModel.updateModel(showTestDescriptionCheckBoxMenuItem.selected) - val idColumn = testOverviewTable.columnModel.getColumn(3) - idColumn.headerValue = testOverviewTableModel.testIdColumnName - testOverviewTable.tableHeader.repaint - } - - private def applyShowWarningIndicator(boolean show) { - val col = testOverviewTable.columnModel.getColumn(1) - if (show) { - col.width = INDICATOR_WIDTH - col.minWidth = INDICATOR_WIDTH - col.maxWidth = INDICATOR_WIDTH - col.preferredWidth = INDICATOR_WIDTH - } else { - col.width = 0 - col.minWidth = 0 - col.maxWidth = 0 - col.preferredWidth = 0 - } - } - - private def applyShowInfoIndicator(boolean show) { - val col = testOverviewTable.columnModel.getColumn(2) - if (show) { - col.width = INDICATOR_WIDTH - col.minWidth = INDICATOR_WIDTH - col.maxWidth = INDICATOR_WIDTH - col.preferredWidth = INDICATOR_WIDTH - } else { - col.width = 0 - col.minWidth = 0 - col.maxWidth = 0 - col.preferredWidth = 0 - } - } - - private def applyFilter(boolean showSuccessfulTests, boolean showDisabledTests) { - val sorter = testOverviewTable.rowSorter as TableRowSorter - val filter = new RowFilter() { - override include(Entry entry) { - val test = entry.model.getTest(entry.identifier) - val counter = test.counter - if (counter !== null) { - if (counter.success > 0) { - if (!showSuccessfulTests) { - return false - } - } - if (counter.disabled > 0) { - if (!showDisabledTests) { - return false - } - } - } - return true - } - } - sorter.rowFilter = filter - } - - private def openTest(Test test) { - val dao = new UtplsqlDao(Connections.instance.getConnection(currentRun.connectionName)) - val source = dao.getSource(test.ownerName, "PACKAGE", test.objectName.toUpperCase).trim - val parser = new UtplsqlParser(source) - val line = parser.getLineOf(test.procedureName) - openEditor(test.ownerName, "PACKAGE", test.objectName.toUpperCase, line, 1) - } - - private def openSelectedTest() { - val rowIndex = testOverviewTable.selectedRow - if (rowIndex != -1) { - val row = testOverviewTable.convertRowIndexToModel(rowIndex) - val test = testOverviewTableModel.getTest(row) - openTest(test) - } - } - - private def openSelectedFailure() { - val rowIndex = failuresTable.selectedRow - if (rowIndex != -1) { - val row = failuresTable.convertRowIndexToModel(rowIndex) - val expectation = failuresTableModel.getExpectation(row) - val test = testOverviewTableModel.getTest(testOverviewTable.convertRowIndexToModel(testOverviewTable.selectedRow)) - val callerLine = expectation.callerLine - if (callerLine !== null) { - openEditor(test.ownerName, "PACKAGE BODY", test.objectName.toUpperCase, expectation.callerLine, 1) - } else { - openTest(test) - } - } - } - - private def getHtml(String text) { - val html = ''' - - - - - - «getLinkedAndFormattedText(text)» - - - ''' - return html - } - - private def openLink(String link) { - val parts = link.split("/") - val type = parts.get(0) - val ownerName = parts.get(1) - val objectName = parts.get(2) - var line = Integer.parseInt(parts.get(3)) - val dao = new UtplsqlDao(Connections.instance.getConnection(currentRun.connectionName)) - val objectType = if (type=="UNKNOWN") {dao.getObjectType(ownerName, objectName)} else {type} - if (parts.size == 5) { - val procedureName = parts.get(4) - val source = dao.getSource(ownerName, objectType, objectName).trim - val parser = new UtplsqlParser(source) - line = parser.getLineOf(procedureName) - } - openEditor(ownerName, objectType, objectName.toUpperCase, line, 1) - } - - private def openEditor(String owner, String type, String name, int line, int col) { - var drillLink = new DefaultDrillLink - drillLink.connName = currentRun.connectionName - // argument order is based on SQLDEV:LINK that can be used in SQL query result tables (editors, reports) - drillLink.args = #[owner, type, name, String.valueOf(line), String.valueOf(col), "OpenEditor", "oracle.dbtools.raptor.controls.grid.DefaultDrillLink"] - drillLink.performDrill - } - - private def syncDetailTab() { - if (syncDetailTabCheckBoxMenuItem.selected) { - val rowIndex = testOverviewTable.selectedRow - if (rowIndex != -1) { - val row = testOverviewTable.convertRowIndexToModel(rowIndex) - val test = testOverviewTableModel.getTest(row) - var int tabIndex - if (test.counter?.failure !== null && test.counter.failure > 0) { - tabIndex = 1 - } else if (test.counter?.error !== null && test.counter.error > 0) { - tabIndex = 2 - } else if (test.counter?.warning !== null && test.counter.warning > 0) { - tabIndex = 3 - } else if (test.serverOutput !== null && test.serverOutput.length > 0) { - tabIndex = 4 - } else { - tabIndex = 0 - } - testDetailTabbedPane.selectedIndex = tabIndex - } - } - } - - private def getPreferenceModel() { - var PreferenceModel preferences - try { - preferences = PreferenceModel.getInstance(Preferences.preferences) - } catch (NoClassDefFoundError e) { - preferences = PreferenceModel.getInstance(null) - } - return preferences - } - - private def applyPreferences() { - val PreferenceModel preferences = preferenceModel - applyShowNumberOfRunsInHistory(preferences.numberOfRunsInHistory) - showDisabledCounterCheckBoxMenuItem.selected = preferences.showDisabledCounter - applyShowDisabledCounter(showDisabledCounterCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showDisabledCounterCheckBoxMenuItem) - showWarningsCounterCheckBoxMenuItem.selected = preferences.showWarningsCounter - applyShowWarningsCounter(showWarningsCounterCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showWarningsCounterCheckBoxMenuItem) - showInfoCounterCheckBoxMenuItem.selected = preferences.showInfoCounter - applyShowInfoCounter(showInfoCounterCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showInfoCounterCheckBoxMenuItem) - showTestDescriptionCheckBoxMenuItem.selected = preferences.showTestDescription - applyShowTestDescription(showTestDescriptionCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showTestDescriptionCheckBoxMenuItem) - showWarningIndicatorCheckBoxMenuItem.selected = preferences.showWarningIndicator - applyShowWarningIndicator(showWarningIndicatorCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showWarningIndicatorCheckBoxMenuItem) - showInfoIndicatorCheckBoxMenuItem.selected = preferences.showInfoIndicator - applyShowInfoIndicator(showInfoIndicatorCheckBoxMenuItem.selected) - showSuccessfulTestsCheckBoxMenuItem.selected = preferences.showSuccessfulTests - fixCheckBoxMenuItem(showSuccessfulTestsCheckBoxMenuItem) - showDisabledTestsCheckBoxMenuItem.selected = preferences.showDisabledTests - fixCheckBoxMenuItem(showDisabledTestsCheckBoxMenuItem) - applyFilter(showSuccessfulTestsCheckBoxMenuItem.selected, showDisabledTestsCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showInfoIndicatorCheckBoxMenuItem) - syncDetailTabCheckBoxMenuItem.selected = preferences.syncDetailTab - fixCheckBoxMenuItem(syncDetailTabCheckBoxMenuItem) - useSmartTimes = preferences.useSmartTimes - } - - def setModel(Run run) { - runs.put(run.reporterId, run) - refreshRunsComboBox - setCurrentRun(run) - } - - private def setCurrentRun(Run run) { - if (run !== currentRun) { - currentRun = run - testOverviewTableModel.setModel(run.tests, showTestDescriptionCheckBoxMenuItem.selected, useSmartTimes) - val header = testOverviewTableModel.timeColumnName - val timeColumn = testOverviewTable.columnModel.getColumn(4) - if (timeColumn.headerValue != header) { - timeColumn.headerValue = header - testOverviewTable.tableHeader.repaint - } - resetDerived - val item = new ComboBoxItem(currentRun.reporterId, currentRun.name) - runComboBox.selectedItem = item - elapsedTimeTimer.start - } - } - - def synchronized update(String reporterId) { - setCurrentRun(runs.get(reporterId)) - val row = currentRun.currentTestNumber - 1 - val header = testOverviewTableModel.testIdColumnName - val idColumn = testOverviewTable.columnModel.getColumn(3) - if (idColumn.headerValue != header) { - idColumn.headerValue = header - testOverviewTable.tableHeader.repaint - } - if (row < 0) { - testOverviewTableModel.fireTableDataChanged - } else { - if (testOverviewTableModel.rowCount > row) { - val positionOfCurrentTest = testOverviewTable.getCellRect(testOverviewTable.convertRowIndexToView(row), 0, true); - testOverviewTable.scrollRectToVisible = positionOfCurrentTest - testOverviewTableModel.fireTableRowsUpdated(row, row) - Thread.sleep(5) // reduce flickering - if (!showSuccessfulTestsCheckBoxMenuItem.selected || !showDisabledTestsCheckBoxMenuItem.selected) { - applyFilter(showSuccessfulTestsCheckBoxMenuItem.selected, showDisabledTestsCheckBoxMenuItem.selected) - } - testOverviewTable.scrollRectToVisible = positionOfCurrentTest - } - } - statusLabel.text = currentRun.status - testCounterValueLabel.text = '''«currentRun.totalNumberOfCompletedTests»«IF currentRun.totalNumberOfTests >= 0»/«currentRun.totalNumberOfTests»«ENDIF»''' - errorCounterValueLabel.text = '''«currentRun.counter.error»''' - failureCounterValueLabel.text = '''«currentRun.counter.failure»''' - disabledCounterValueLabel.text = '''«currentRun.counter.disabled»''' - warningsCounterValueLabel.text = '''«currentRun.counter.warning»''' - infoCounterValueLabel.text = '''«currentRun.infoCount»''' - if (currentRun.totalNumberOfTests == 0) { - progressBar.value = 100 - } else { - progressBar.value = Math.round(100 * currentRun.totalNumberOfCompletedTests / currentRun.totalNumberOfTests) - } - if (currentRun.counter.error > 0 || currentRun.counter.failure > 0) { - progressBar.foreground = RED - } else { - progressBar.foreground = GREEN - } - } - - private def getPathListFromSelectedTests() { - val pathList = new ArrayList - for (rowIndex : testOverviewTable.selectedRows) { - val row = testOverviewTable.convertRowIndexToModel(rowIndex) - val test = testOverviewTableModel.getTest(row) - val path = '''«test.ownerName».«test.objectName».«test.procedureName»''' - pathList.add(path) - } - return pathList - } - - private def isWindowsLookAndFeel() { - val laf = UIManager.lookAndFeel?.name - if (laf == "Windows") { - return true - } else { - return false - } - } - - private def isMacLookAndFeel() { - val laf = UIManager.lookAndFeel?.name - if (laf == "Mac OS X") { - return true - } else { - return false - } - } - - private def void fixCheckBoxMenuItem(JCheckBoxMenuItem item) { - if (windowsLookAndFeel) { - if (item.selected) { - item.icon = UtplsqlResources.getIcon("CHECKMARK_ICON") - } else { - item.icon = null - } - } - } - - override actionPerformed(ActionEvent e) { - if (e.source == refreshButton) { - resetDerived - testDetailTabbedPane.selectedIndex = 0 - testOverviewTableModel.fireTableDataChanged - } else if (e.source == rerunButton) { - val runner = new UtplsqlRunner(currentRun.pathList, currentRun.connectionName) - runner.runTestAsync - } else if (e.source == rerunWorksheetButton) { - val worksheet = new UtplsqlWorksheetRunner(currentRun.pathList, currentRun.connectionName) - worksheet.runTestAsync - } else if (e.source == runComboBox) { - if (currentRun !== null) { - val comboBoxItem = runComboBox.selectedItem as ComboBoxItem - if (currentRun.reporterId != comboBoxItem.key) { - update(comboBoxItem.key) - testDetailTabbedPane.selectedIndex = 0 - } - } - } else if (e.source == clearButton) { - val run = currentRun - runs.clear - currentRun = null - setModel(run) - update(run.reporterId) - } else if (e.source == testOverviewRunMenuItem) { - val runner = new UtplsqlRunner(pathListFromSelectedTests, currentRun.connectionName) - runner.runTestAsync - } else if (e.source == testOverviewRunWorksheetMenuItem) { - val worksheet = new UtplsqlWorksheetRunner(pathListFromSelectedTests, currentRun.connectionName) - worksheet.runTestAsync - } else if (e.source == showDisabledCounterCheckBoxMenuItem) { - applyShowDisabledCounter(showDisabledCounterCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showDisabledCounterCheckBoxMenuItem) - } else if (e.source == showWarningsCounterCheckBoxMenuItem) { - applyShowWarningsCounter( showWarningsCounterCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showWarningsCounterCheckBoxMenuItem) - } else if (e.source == showInfoCounterCheckBoxMenuItem) { - applyShowInfoCounter(showInfoCounterCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showInfoCounterCheckBoxMenuItem) - } else if (e.source == showSuccessfulTestsCheckBoxMenuItem) { - applyFilter(showSuccessfulTestsCheckBoxMenuItem.selected, showDisabledTestsCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showSuccessfulTestsCheckBoxMenuItem) - } else if (e.source == showDisabledTestsCheckBoxMenuItem) { - applyFilter(showSuccessfulTestsCheckBoxMenuItem.selected, showDisabledTestsCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showDisabledTestsCheckBoxMenuItem) - } else if (e.source == showTestDescriptionCheckBoxMenuItem) { - applyShowTestDescription(showTestDescriptionCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showTestDescriptionCheckBoxMenuItem) - } else if (e.source == showWarningIndicatorCheckBoxMenuItem) { - applyShowWarningIndicator(showWarningIndicatorCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showWarningIndicatorCheckBoxMenuItem) - } else if (e.source == showInfoIndicatorCheckBoxMenuItem) { - applyShowInfoIndicator(showInfoIndicatorCheckBoxMenuItem.selected) - fixCheckBoxMenuItem(showInfoIndicatorCheckBoxMenuItem) - } else if (e.source == syncDetailTabCheckBoxMenuItem) { - syncDetailTab - fixCheckBoxMenuItem(syncDetailTabCheckBoxMenuItem) - } - } - - override mouseClicked(MouseEvent e) { - if (e.clickCount == 2) { - if (e.source == testOverviewTable) { - if (failuresTable.selectedRowCount == 1) { - openSelectedFailure - } else { - openSelectedTest - } - - } else if (e.source == failuresTable) { - if (failuresTable.selectedRowCount == 1) { - openSelectedFailure - } - } - } - } - - override mouseEntered(MouseEvent e) { - } - - override mouseExited(MouseEvent e) { - } - - override mousePressed(MouseEvent e) { - } - - override mouseReleased(MouseEvent e) { - } - - override hyperlinkUpdate(HyperlinkEvent e) { - if (e.eventType == HyperlinkEvent.EventType.ACTIVATED) { - val link = e.description - openLink(link) - } - } - - private static def formatDateTime(String dateTime) { - if (dateTime === null) { - return null - } else { - if (dateTime.length == 26) { - return dateTime.replace("T", " ").substring(0, 23) - } else { - return dateTime - } - } - } - - static class TestOverviewRowListener implements ListSelectionListener { - RunnerPanel p - - new (RunnerPanel p) { - this.p = p - } - - override void valueChanged(ListSelectionEvent event) { - val rowIndex = p.testOverviewTable.selectedRow - if (rowIndex != -1) { - val row = p.testOverviewTable.convertRowIndexToModel(rowIndex) - val test = p.testOverviewTableModel.getTest(row) - p.testOwnerTextField.text = test.ownerName - p.testPackageTextField.text = test.objectName - p.testProcedureTextField.text = test.procedureName - p.testDescriptionTextArea.text = test.description?.trim - p.testIdTextArea.text = test.id - p.testStartTextField.text = formatDateTime(test.startTime) - p.failuresTableModel.model = test.failedExpectations - p.failuresTableModel.fireTableDataChanged - p.testFailureMessageTextPane.text = null - if (test.failedExpectations !== null && test.failedExpectations.size > 0) { - p.failuresTable.setRowSelectionInterval(0, 0) - } - p.testErrorStackTextPane.text = p.getHtml(test.errorStack?.trim) - p.testWarningsTextPane.text = p.getHtml(test.warnings?.trim) - p.testServerOutputTextPane.text = p.getHtml(test.serverOutput?.trim) - p.syncDetailTab - p.testOverviewRunMenuItem.enabled = true - p.testOverviewRunWorksheetMenuItem.enabled = true - } - } - } - - private def getLinkedAndFormattedText(String text) { - if (text === null) { - return "" - } - // Patterns (primarily Asserts, Errors, ServerOutput): - // at "OWNER.PACKAGE.PROCEDURE", line 42 - // at "OWNER.PROCEDURE", line 42 - // at "OWNER.PACKAGE", line 42 - // at package "OWNER.PACKAGE", line 42 - val p1 = Pattern.compile('''\s+(package\s+)?("(\S+?)\.(\S+?)(?:\.(\S+?))?",\s+line\s+([0-9]+))''') - var localText = HtmlUtils.htmlEscape(text) - var m = p1.matcher(localText) - while(m.find) { - val link = '''«m.group(2)»''' - val start = m.start(2) - val end = m.end(2) - localText = '''«localText.substring(0, start)»«link»«localText.substring(end)»''' - m = p1.matcher(localText) - } - // Patterns (primarily Warnings, without line reference, calculate when opening link): - // owner.package.procedure - val p2 = Pattern.compile('''^\s{2}((\S+?)\.(\S+?)\.(\S+?))$''', Pattern.MULTILINE) - m = p2.matcher(localText) - while(m.find) { - val link = '''  «m.group(1)»''' - val start = m.start(0) - val end = m.end(0) - localText = '''«localText.substring(0, start)»«link»«localText.substring(end)»''' - m = p2.matcher(localText) - } - // Patterns (Title for warning/info on suite level) - // from suite a.junit_utplsql_test1_pkg: - val p3 = Pattern.compile('''^For suite ([^:]+):$''', Pattern.MULTILINE) - m = p3.matcher(localText) - while(m.find) { - val title = '''For suite "«m.group(1)»"''' - val start = m.start(0) - val end = m.end(0) - localText = '''«localText.substring(0, start)»«title»«localText.substring(end)»''' - m = p3.matcher(localText) - } - val result = ''' - «FOR p : localText.split("\n")» -

«p»

- «ENDFOR» - ''' - return result - } - - static class FailuresRowListener implements ListSelectionListener { - RunnerPanel p - - new (RunnerPanel p) { - this.p = p - } - - override void valueChanged(ListSelectionEvent event) { - val rowIndex = p.failuresTable.selectedRow - if (rowIndex != -1) { - val row = p.failuresTable.convertRowIndexToModel(rowIndex) - val expectation = p.failuresTableModel.getExpectation(row) - val html = p.getHtml(expectation.failureText) - p.testFailureMessageTextPane.text = html - - } - } - } - - static class TimeFormatRenderer extends DefaultTableCellRenderer { - override getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, - int row, int col) { - val smartTime = new SmartTime(value as Double, useSmartTimes) - return super.getTableCellRendererComponent(table, smartTime.toString, isSelected, hasFocus, row, col) - } - } - - static class TestTableHeaderRenderer extends DefaultTableCellRenderer { - override getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, - int row, int col) { - val renderer = table.tableHeader.defaultRenderer - val label = renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col) as JLabel - if (col === 0) { - label.icon = UtplsqlResources.getIcon("STATUS_ICON") - label.horizontalAlignment = JLabel.CENTER - } else if (col === 1) { - label.icon = UtplsqlResources.getIcon("WARNING_ICON") - label.horizontalAlignment = JLabel.CENTER - } else if (col === 2) { - label.icon = UtplsqlResources.getIcon("INFO_ICON") - label.horizontalAlignment = JLabel.CENTER - } else if (col === 3) { - label.icon = null - label.horizontalAlignment = JLabel.LEFT - } else if (col === 4) { - label.icon = null - label.horizontalAlignment = JLabel.RIGHT - } - return label - } - } - - static class FailuresTableHeaderRenderer extends DefaultTableCellRenderer { - override getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, - int row, int col) { - val renderer = table.tableHeader.defaultRenderer - val label = renderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col) as JLabel - if (col === 0) { - label.horizontalAlignment = JLabel.RIGHT - } else { - label.horizontalAlignment = JLabel.LEFT - } - return label - } - } - - private def makeLabelledCounterComponent (JLabel label, JComponent comp) { - val groupPanel = new JPanel - groupPanel.layout = new GridBagLayout - var GridBagConstraints c = new GridBagConstraints - // label - c.gridx = 0 - c.gridy = 0 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(5, 10, 5, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::NONE - c.weightx = 0 - c.weighty = 0 - groupPanel.add(label, c) - // component - c.gridx = 1 - c.gridy = 0 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(5, 5, 5, 10) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::HORIZONTAL - c.weightx = 1 - c.weighty = 0 - groupPanel.add(comp, c) - val dim = new Dimension(134, 24) - groupPanel.minimumSize = dim - groupPanel.preferredSize = dim - return groupPanel - } - - private def initializeGUI() { - // Base panel containing all components - basePanel = new JPanel() - basePanel.setLayout(new GridBagLayout()) - var GridBagConstraints c = new GridBagConstraints() - - // Toolbar - var toolbar = new GradientToolbar - toolbar.floatable = false - val buttonBorder = new EmptyBorder(new Insets(2, 4, 2, 4)) // top, left, bottom, right - refreshButton = new ToolbarButton(UtplsqlResources.getIcon("REFRESH_ICON")) - refreshButton.toolTipText = UtplsqlResources.getString("RUNNER_REFRESH_TOOLTIP") - refreshButton.border = buttonBorder - refreshButton.addActionListener(this) - toolbar.add(refreshButton) - rerunButton = new ToolbarButton(UtplsqlResources.getIcon("RUN_ICON")) - rerunButton.toolTipText = UtplsqlResources.getString("RUNNER_RERUN_TOOLTIP") - rerunButton.border = buttonBorder - rerunButton.addActionListener(this) - toolbar.add(rerunButton) - rerunWorksheetButton = new ToolbarButton(UtplsqlResources.getIcon("RUN_WORKSHEET_ICON")) - rerunWorksheetButton.toolTipText = UtplsqlResources.getString("RUNNER_RERUN_WORKSHEET_TOOLTIP") - rerunWorksheetButton.border = buttonBorder - rerunWorksheetButton.addActionListener(this) - toolbar.add(rerunWorksheetButton) - toolbar.add(Box.createHorizontalGlue()) - runComboBoxModel = new DefaultComboBoxModel>; - runComboBox = new JComboBox>(runComboBoxModel); - runComboBox.editable = false - val comboBoxDim = new Dimension(500, 50) - runComboBox.maximumSize = comboBoxDim - runComboBox.addActionListener(this) - toolbar.add(runComboBox) - clearButton = new ToolbarButton(UtplsqlResources.getIcon("CLEAR_ICON")) - clearButton.toolTipText = UtplsqlResources.getString("RUNNER_CLEAR_BUTTON") - clearButton.border = buttonBorder - clearButton.addActionListener(this) - toolbar.add(clearButton) - c.gridx = 0 - c.gridy = 0 - c.gridwidth = 2 - c.gridheight = 1 - c.insets = new Insets(0, 0, 0, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::NORTH - c.fill = GridBagConstraints::HORIZONTAL - c.weightx = 1 - c.weighty = 0 - basePanel.add(toolbar, c) - - // Status line - statusLabel = new JLabel - c.gridx = 0 - c.gridy = 1 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(10, 10, 10, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::HORIZONTAL - c.weightx = 1 - c.weighty = 0 - basePanel.add(statusLabel, c) - elapsedTimeLabel = new JLabel - elapsedTimeLabel.preferredSize = new Dimension(60, 0) - c.gridx = 1 - c.gridy = 1 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(10, 10, 10, 10) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::NONE - c.weightx = 0 - c.weighty = 0 - basePanel.add(elapsedTimeLabel, c) - elapsedTimeTimer = new Timer(100, new ActionListener() { - override actionPerformed(ActionEvent e) { - if (currentRun !== null && currentRun.start !== null) { - val time = new SmartTime - time.smart = useSmartTimes - if (currentRun.executionTime !== null) { - time.seconds = currentRun.executionTime - elapsedTimeTimer.stop - } else { - val long now = System.currentTimeMillis - time.seconds = new Double(now - currentRun.start) / 1000 - } - elapsedTimeLabel.text = '''«time.toString»«IF !useSmartTimes» s«ENDIF»''' - } else { - elapsedTimeLabel.text = null - } - } - }) - - // Counters - // - Test counter - val counterPanel = new JPanel - counterPanel.layout = new WrapLayout(FlowLayout.LEFT, 0, 0) - val testCounterLabel = new JLabel(UtplsqlResources.getString("RUNNER_TESTS_LABEL") + ":", JLabel::LEADING) - testCounterValueLabel = new JLabel - counterPanel.add(makeLabelledCounterComponent(testCounterLabel, testCounterValueLabel)) - // - Failure counter - val failureCounterLabel = new JLabel(UtplsqlResources.getString("RUNNER_FAILURES_LABEL") + ":", - UtplsqlResources.getIcon("FAILURE_ICON"), JLabel::LEADING) - failureCounterValueLabel = new JLabel - counterPanel.add(makeLabelledCounterComponent(failureCounterLabel,failureCounterValueLabel)) - // - Error counter - val errorCounterLabel = new JLabel(UtplsqlResources.getString("RUNNER_ERRORS_LABEL") + ":", - UtplsqlResources.getIcon("ERROR_ICON"), JLabel::LEADING) - errorCounterValueLabel = new JLabel - counterPanel.add(makeLabelledCounterComponent(errorCounterLabel, errorCounterValueLabel)) - // - Disabled counter - val disabledCounterLabel = new JLabel(UtplsqlResources.getString("RUNNER_DISABLED_LABEL") + ":", - UtplsqlResources.getIcon("DISABLED_ICON"), JLabel::LEADING) - disabledCounterValueLabel = new JLabel - counterPanel.add(makeLabelledCounterComponent(disabledCounterLabel, disabledCounterValueLabel)) - // - Warnings counter - val warningsCounterLabel = new JLabel(UtplsqlResources.getString("RUNNER_WARNINGS_LABEL") + ":", - UtplsqlResources.getIcon("WARNING_ICON"), JLabel::LEADING) - warningsCounterValueLabel = new JLabel - counterPanel.add(makeLabelledCounterComponent(warningsCounterLabel, warningsCounterValueLabel)) - // - Info counter - val infoCounterLabel = new JLabel(UtplsqlResources.getString("RUNNER_INFO_LABEL") + ":", - UtplsqlResources.getIcon("INFO_ICON"), JLabel::LEADING) - infoCounterValueLabel = new JLabel - counterPanel.add(makeLabelledCounterComponent(infoCounterLabel, infoCounterValueLabel)) - // - add everything to basePanel - c.gridx = 0 - c.gridy = 2 - c.gridwidth = 2 - c.gridheight = 1 - c.insets = new Insets(5, 0, 5, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::HORIZONTAL - c.weightx = 1 - c.weighty = 0 - basePanel.add(counterPanel,c) - - // Context menu for counters panel - val countersPopupMenu = new JPopupMenu - showDisabledCounterCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_DISABLED_COUNTER_LABEL").replace("?",""), true) - showDisabledCounterCheckBoxMenuItem.addActionListener(this) - countersPopupMenu.add(showDisabledCounterCheckBoxMenuItem) - showWarningsCounterCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_WARNINGS_COUNTER_LABEL").replace("?",""), true) - showWarningsCounterCheckBoxMenuItem.addActionListener(this) - countersPopupMenu.add(showWarningsCounterCheckBoxMenuItem) - showInfoCounterCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_INFO_COUNTER_LABEL").replace("?",""), true) - showInfoCounterCheckBoxMenuItem.addActionListener(this) - countersPopupMenu.add(showInfoCounterCheckBoxMenuItem) - counterPanel.componentPopupMenu = countersPopupMenu - - // Progress bar - progressBar = new JProgressBar - val progressBarDim = new Dimension(10, 20) - progressBar.preferredSize = progressBarDim - progressBar.minimumSize = progressBarDim - progressBar.stringPainted = false - progressBar.foreground = GREEN - progressBar.UI = new BasicProgressBarUI - c.gridx = 0 - c.gridy = 3 - c.gridwidth = 2 - c.gridheight = 1 - c.insets = new Insets(10, 10, 10, 10) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::HORIZONTAL - c.weightx = 1 - c.weighty = 0 - basePanel.add(progressBar, c) - - // Test overview - testOverviewTableModel = new TestOverviewTableModel - testOverviewTable = new JTable(testOverviewTableModel) - testOverviewTable.tableHeader.reorderingAllowed = false - testOverviewTable.autoCreateRowSorter = true - testOverviewTable.rowHeight = OVERVIEW_TABLE_ROW_HEIGHT - testOverviewTable.tableHeader.preferredSize = new Dimension(testOverviewTable.tableHeader.getPreferredSize.width, OVERVIEW_TABLE_ROW_HEIGHT) - testOverviewTable.selectionModel.addListSelectionListener(new TestOverviewRowListener(this)) - testOverviewTable.addMouseListener(this) - RepaintManager.currentManager(testOverviewTable).doubleBufferingEnabled = true // reduce flickering - val testTableHeaderRenderer = new TestTableHeaderRenderer - val overviewTableStatus = testOverviewTable.columnModel.getColumn(0) - overviewTableStatus.minWidth = INDICATOR_WIDTH - overviewTableStatus.preferredWidth = INDICATOR_WIDTH - overviewTableStatus.maxWidth = INDICATOR_WIDTH - overviewTableStatus.headerRenderer = testTableHeaderRenderer - val overviewTableWarning = testOverviewTable.columnModel.getColumn(1) - overviewTableWarning.minWidth = INDICATOR_WIDTH - overviewTableWarning.preferredWidth = INDICATOR_WIDTH - overviewTableWarning.maxWidth = INDICATOR_WIDTH - overviewTableWarning.headerRenderer = testTableHeaderRenderer - val overviewTableInfo = testOverviewTable.columnModel.getColumn(2) - overviewTableInfo.minWidth = INDICATOR_WIDTH - overviewTableInfo.preferredWidth = INDICATOR_WIDTH - overviewTableInfo.maxWidth = INDICATOR_WIDTH - overviewTableInfo.headerRenderer = testTableHeaderRenderer - val overviewTableId = testOverviewTable.columnModel.getColumn(3) - overviewTableId.headerRenderer = testTableHeaderRenderer - val overviewTableTime = testOverviewTable.columnModel.getColumn(4) - overviewTableTime.preferredWidth = 60 - overviewTableTime.maxWidth = 100 - overviewTableTime.headerRenderer = testTableHeaderRenderer - val timeFormatRenderer = new TimeFormatRenderer - timeFormatRenderer.horizontalAlignment = JLabel.RIGHT - overviewTableTime.cellRenderer = timeFormatRenderer - val testOverviewScrollPane = new JScrollPane(testOverviewTable) - - // Context menu for test overview - val testOverviewPopupMenu = new JPopupMenu - testOverviewRunMenuItem = new JMenuItem(UtplsqlResources.getString("RUNNER_RUN_MENUITEM"), UtplsqlResources.getIcon("RUN_ICON")); - testOverviewRunMenuItem.addActionListener(this) - testOverviewPopupMenu.add(testOverviewRunMenuItem) - testOverviewRunWorksheetMenuItem = new JMenuItem(UtplsqlResources.getString("RUNNER_RUN_WORKSHEET_MENUITEM"), UtplsqlResources.getIcon("RUN_WORKSHEET_ICON")); - testOverviewRunWorksheetMenuItem.addActionListener(this) - testOverviewPopupMenu.add(testOverviewRunWorksheetMenuItem) - testOverviewPopupMenu.add(new JSeparator) - showSuccessfulTestsCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_SUCCESSFUL_TESTS_LABEL").replace("?",""), true) - showSuccessfulTestsCheckBoxMenuItem.addActionListener(this) - testOverviewPopupMenu.add(showSuccessfulTestsCheckBoxMenuItem) - showDisabledTestsCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_DISABLED_TESTS_LABEL").replace("?",""), true) - showDisabledTestsCheckBoxMenuItem.addActionListener(this) - testOverviewPopupMenu.add(showDisabledTestsCheckBoxMenuItem) - testOverviewPopupMenu.add(new JSeparator) - showTestDescriptionCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_TEST_DESCRIPTION_LABEL").replace("?",""), true) - showTestDescriptionCheckBoxMenuItem.addActionListener(this) - testOverviewPopupMenu.add(showTestDescriptionCheckBoxMenuItem) - showWarningIndicatorCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_WARNING_INDICATOR_LABEL").replace("?",""), true) - showWarningIndicatorCheckBoxMenuItem.addActionListener(this) - testOverviewPopupMenu.add(showWarningIndicatorCheckBoxMenuItem) - showInfoIndicatorCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_INFO_INDICATOR_LABEL").replace("?",""), true) - showInfoIndicatorCheckBoxMenuItem.addActionListener(this) - testOverviewPopupMenu.add(showInfoIndicatorCheckBoxMenuItem) - syncDetailTabCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SYNC_DETAIL_TAB_LABEL").replace("?",""), true) - syncDetailTabCheckBoxMenuItem.addActionListener(this) - testOverviewPopupMenu.add(syncDetailTabCheckBoxMenuItem) - testOverviewTable.componentPopupMenu = testOverviewPopupMenu - testOverviewTable.tableHeader.componentPopupMenu = testOverviewPopupMenu - - // Test tabbed pane (Test Properties) - val testInfoPanel = new ScrollablePanel - testInfoPanel.setLayout(new GridBagLayout()) - // - Owner - val testOwnerLabel = new JLabel(UtplsqlResources.getString("RUNNER_OWNER_LABEL")) - c.gridx = 0 - c.gridy = 0 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(10, 10, 0, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::NONE - c.weightx = 0 - c.weighty = 0 - testInfoPanel.add(testOwnerLabel, c) - testOwnerTextField = new RunnerTextField - testOwnerTextField.editable = false - c.gridx = 1 - c.gridy = 0 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(10, 5, 0, 10) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::HORIZONTAL - c.weightx = 1 - c.weighty = 0 - testInfoPanel.add(testOwnerTextField, c) - // - Package - val testPackageLabel = new JLabel(UtplsqlResources.getString("RUNNER_PACKAGE_LABEL")) - c.gridx = 0 - c.gridy = 1 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(5, 10, 0, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::NONE - c.weightx = 0 - c.weighty = 0 - testInfoPanel.add(testPackageLabel, c) - testPackageTextField = new RunnerTextField - testPackageTextField.editable = false - c.gridx = 1 - c.gridy = 1 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(5, 5, 0, 10) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::HORIZONTAL - c.weightx = 1 - c.weighty = 0 - testInfoPanel.add(testPackageTextField, c) - // - Procedure - val testProcedureLabel = new JLabel(UtplsqlResources.getString("RUNNER_PROCEDURE_LABEL")) - c.gridx = 0 - c.gridy = 2 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(5, 10, 0, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::NONE - c.weightx = 0 - c.weighty = 0 - testInfoPanel.add(testProcedureLabel, c) - testProcedureTextField = new RunnerTextField - testProcedureTextField.editable = false - c.gridx = 1 - c.gridy = 2 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(5, 5, 0, 10) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::HORIZONTAL - c.weightx = 1 - c.weighty = 0 - testInfoPanel.add(testProcedureTextField, c) - // - Description - val testDescriptionLabel = new JLabel(UtplsqlResources.getString("RUNNER_DESCRIPTION_LABEL")) - testDescriptionLabel.border = BorderFactory.createEmptyBorder(if (macLookAndFeel) {5} else {3}, 0, 0, 0) - c.gridx = 0 - c.gridy = 3 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(5, 10, 0, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::NORTHWEST - c.fill = GridBagConstraints::NONE - c.weightx = 0 - c.weighty = 0 - testInfoPanel.add(testDescriptionLabel, c) - testDescriptionTextArea = new RunnerTextArea - testDescriptionTextArea.editable = false - testDescriptionTextArea.enabled = true - testDescriptionTextArea.lineWrap = true - testDescriptionTextArea.wrapStyleWord = true - c.gridx = 1 - c.gridy = 3 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(5, 5, 0, 10) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::HORIZONTAL - c.weightx = 1 - c.weighty = 0 - testInfoPanel.add(testDescriptionTextArea, c) - // - Suitepath (id) - val testIdLabel = new JLabel(UtplsqlResources.getString("RUNNER_TEST_ID_COLUMN")) - testIdLabel.border = BorderFactory.createEmptyBorder(if (macLookAndFeel) {5} else {3}, 0, 0, 0) - c.gridx = 0 - c.gridy = 4 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(5, 10, 0, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::NORTHWEST - c.fill = GridBagConstraints::NONE - c.weightx = 0 - c.weighty = 0 - testInfoPanel.add(testIdLabel, c) - testIdTextArea = new RunnerTextArea - testIdTextArea.editable = false - testIdTextArea.enabled = true - testIdTextArea.lineWrap = true - testIdTextArea.wrapStyleWord = false - c.gridx = 1 - c.gridy = 4 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(5, 5, 0, 10) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::HORIZONTAL - c.weightx = 1 - c.weighty = 0 - testInfoPanel.add(testIdTextArea, c) - // - Start - val testStartLabel = new JLabel(UtplsqlResources.getString("RUNNER_START_LABEL")) - c.gridx = 0 - c.gridy = 5 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(5, 10, 10, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::NONE - c.weightx = 0 - c.weighty = 0 - testInfoPanel.add(testStartLabel, c) - testStartTextField = new RunnerTextField - testStartTextField.editable = false - c.gridx = 1 - c.gridy = 5 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(5, 5, 10, 10) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::HORIZONTAL - c.weightx = 1 - c.weighty = 0 - testInfoPanel.add(testStartTextField, c) - // - Vertical spring and scrollbar for info panel - c.gridx = 0 - c.gridy = 6 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(0, 0, 0, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::BOTH - c.weightx = 0 - c.weighty = 1 - testInfoPanel.add(Box.createVerticalGlue(), c) - val testPropertiesScrollPane = new JScrollPane(testInfoPanel) - - // Failures tabbed pane (failed expectations) - // - failures table (number and description) - failuresTableModel = new FailuresTableModel - failuresTable = new JTable(failuresTableModel) - failuresTable.tableHeader.reorderingAllowed = false - failuresTable.selectionModel.addListSelectionListener(new FailuresRowListener(this)) - failuresTable.addMouseListener(this) - val failuresTableHeaderRenderer = new FailuresTableHeaderRenderer - val failuresTableNumber = failuresTable.columnModel.getColumn(0) - failuresTableNumber.headerRenderer = failuresTableHeaderRenderer - failuresTableNumber.preferredWidth = 30 - failuresTableNumber.maxWidth = 30 - val failuresDescription = failuresTable.columnModel.getColumn(1) - failuresDescription.headerRenderer = failuresTableHeaderRenderer - val failuresTableScrollPane = new JScrollPane(failuresTable) - // - failures details - testFailureMessageTextPane = new RunnerTextPane - testFailureMessageTextPane.editable = false - testFailureMessageTextPane.enabled = true - testFailureMessageTextPane.contentType = "text/html" - testFailureMessageTextPane.minimumSize = TEXTPANE_DIM - testFailureMessageTextPane.preferredSize = TEXTPANE_DIM - testFailureMessageTextPane.addHyperlinkListener(this) - val testFailureMessageScrollPane = new JScrollPane(testFailureMessageTextPane) - c.gridx = 1 - c.gridy = 0 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(10, 5, 0, 10) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::BOTH - c.weightx = 1 - c.weighty = 6 - - // - split pane - val failuresSplitPane = new JSplitPane(SwingConstants.HORIZONTAL, failuresTableScrollPane, testFailureMessageScrollPane) - failuresSplitPane.resizeWeight = 0.2 - - // Errors tabbed pane (Error Stack) - val testErrorStackPanel = new JPanel - testErrorStackPanel.setLayout(new GridBagLayout()) - testErrorStackTextPane = new RunnerTextPane - testErrorStackTextPane.editable = false - testErrorStackTextPane.enabled = true - testErrorStackTextPane.contentType = "text/html" - testErrorStackTextPane.minimumSize = TEXTPANE_DIM - testErrorStackTextPane.preferredSize = TEXTPANE_DIM - testErrorStackTextPane.addHyperlinkListener(this) - val testErrorStackScrollPane = new JScrollPane(testErrorStackTextPane) - c.gridx = 0 - c.gridy = 0 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(0, 0, 0, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::BOTH - c.weightx = 1 - c.weighty = 1 - testErrorStackPanel.add(testErrorStackScrollPane, c) - - // Warnings tabbed pane - val testWarningsPanel = new JPanel - testWarningsPanel.setLayout(new GridBagLayout()) - testWarningsTextPane = new RunnerTextPane - testWarningsTextPane.editable = false - testWarningsTextPane.enabled = true - testWarningsTextPane.contentType = "text/html" - testWarningsTextPane.minimumSize = TEXTPANE_DIM - testWarningsTextPane.preferredSize = TEXTPANE_DIM - testWarningsTextPane.addHyperlinkListener(this) - val testWarningsScrollPane = new JScrollPane(testWarningsTextPane) - c.gridx = 0 - c.gridy = 0 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(0, 0, 0, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::BOTH - c.weightx = 1 - c.weighty = 1 - testWarningsPanel.add(testWarningsScrollPane, c) - - // Info tabbed pane (Server Output) - val testServerOutputPanel = new JPanel - testServerOutputPanel.setLayout(new GridBagLayout()) - testServerOutputTextPane = new RunnerTextPane - testServerOutputTextPane.editable = false - testServerOutputTextPane.enabled = true - testServerOutputTextPane.contentType = "text/html" - testServerOutputTextPane.minimumSize = TEXTPANE_DIM - testServerOutputTextPane.preferredSize = TEXTPANE_DIM - testServerOutputTextPane.addHyperlinkListener(this) - val testServerOutputScrollPane = new JScrollPane(testServerOutputTextPane) - c.gridx = 0 - c.gridy = 0 - c.gridwidth = 1 - c.gridheight = 1 - c.insets = new Insets(0, 0, 0, 0) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::BOTH - c.weightx = 1 - c.weighty = 1 - testServerOutputPanel.add(testServerOutputScrollPane, c) - - // split pane with all tabs - testDetailTabbedPane = new JTabbedPane() - testDetailTabbedPane.add(UtplsqlResources.getString("RUNNER_TEST_TAB_LABEL"), testPropertiesScrollPane) - testDetailTabbedPane.add(UtplsqlResources.getString("RUNNER_FAILURES_TAB_LABEL"), failuresSplitPane) - testDetailTabbedPane.add(UtplsqlResources.getString("RUNNER_ERRORS_TAB_LABEL"), testErrorStackPanel) - testDetailTabbedPane.add(UtplsqlResources.getString("RUNNER_WARNINGS_TAB_LABEL"), testWarningsPanel) - testDetailTabbedPane.add(UtplsqlResources.getString("RUNNER_INFO_TAB_LABEL"), testServerOutputPanel) - val horizontalSplitPane = new JSplitPane(SwingConstants.HORIZONTAL, testOverviewScrollPane, testDetailTabbedPane) - horizontalSplitPane.resizeWeight = 0.5 - c.gridx = 0 - c.gridy = 4 - c.gridwidth = 2 - c.gridheight = 1 - c.insets = new Insets(10, 10, 10, 10) // top, left, bottom, right - c.anchor = GridBagConstraints::WEST - c.fill = GridBagConstraints::BOTH - c.weightx = 1 - c.weighty = 1 - basePanel.add(horizontalSplitPane, c) - - // fix borders (colors, margins) - if (macLookAndFeel) { - val border = BorderFactory.createCompoundBorder( - BorderFactory.createEmptyBorder(3, 3, 3, 3), - BorderFactory.createCompoundBorder( - BorderFactory.createLineBorder(new Color(219, 219, 219)), - BorderFactory.createEmptyBorder(1, 1, 1, 1) - ) - ) - testDescriptionTextArea.border = border - testIdTextArea.border = border - } else { - val referenceBorder = testOwnerTextField.border - testDescriptionTextArea.border = referenceBorder - testIdTextArea.border = referenceBorder - } - } - -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextArea.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextArea.java new file mode 100644 index 00000000..25377d1d --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextArea.java @@ -0,0 +1,60 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.runner; + +import java.awt.Graphics; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +import javax.swing.JTextArea; +import javax.swing.UIManager; + +public class RunnerTextArea extends JTextArea implements FocusListener { + private static final long serialVersionUID = -1536393556223117580L; + + public RunnerTextArea() { + super(); + addFocusListener(this); + } + + @Override + public void paintComponent(final Graphics g) { + // default for non-opaque components + if (!isOpaque()) { + super.paintComponent(g); + return; + } + + // use value of JTextField for consistency + g.setColor(UIManager.getColor("TextField.inactiveBackground")); + g.fillRect(3, 3, getWidth() - 6, getHeight() - 6); + + // do rest, changing opaque to ensure background is not overwritten + setOpaque(false); + super.paintComponent(g); + setOpaque(true); + } + + @Override + public void focusGained(final FocusEvent e) { + getCaret().setVisible(true); + } + + @Override + public void focusLost(final FocusEvent e) { + getCaret().setVisible(false); + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextArea.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextArea.xtend deleted file mode 100644 index be9520c7..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextArea.xtend +++ /dev/null @@ -1,40 +0,0 @@ -package org.utplsql.sqldev.ui.runner - -import java.awt.Graphics -import java.awt.event.FocusEvent -import java.awt.event.FocusListener -import javax.swing.JTextArea -import javax.swing.UIManager - -class RunnerTextArea extends JTextArea implements FocusListener{ - - new() { - super() - this.addFocusListener = this - } - - override paintComponent(Graphics g) { - // default for non-opaque components - if (!opaque) { - super.paintComponent(g) - return - } - - // use value of JTextField for consistency - g.color = UIManager.getColor("TextField.inactiveBackground") - g.fillRect(3, 3, width - 6, height - 6) - - // do rest, changing opaque to ensure background is not overwritten - setOpaque(false) - super.paintComponent(g) - setOpaque(true) - } - - override void focusGained(FocusEvent e) { - this.caret.visible = true - } - - override focusLost(FocusEvent e) { - this.caret.visible = false - } -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextField.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextField.java new file mode 100644 index 00000000..0553ab5e --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextField.java @@ -0,0 +1,60 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.runner; + +import java.awt.Graphics; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +import javax.swing.JTextField; +import javax.swing.UIManager; + +public class RunnerTextField extends JTextField implements FocusListener { + private static final long serialVersionUID = 4527406698634871523L; + + public RunnerTextField() { + super(); + addFocusListener(this); + } + + @Override + public void paintComponent(final Graphics g) { + // default for non-opaque components + if (!isOpaque()) { + super.paintComponent(g); + return; + } + + // use value of JTextField for consistency + g.setColor(UIManager.getColor("TextField.inactiveBackground")); + g.fillRect(0, 0, getWidth(), getHeight()); + + // do rest, changing opaque to ensure background is not overwritten + setOpaque(false); + super.paintComponent(g); + setOpaque(true); + } + + @Override + public void focusGained(final FocusEvent e) { + getCaret().setVisible(true); + } + + @Override + public void focusLost(final FocusEvent e) { + getCaret().setVisible(false); + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextField.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextField.xtend deleted file mode 100644 index 65e326ea..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextField.xtend +++ /dev/null @@ -1,39 +0,0 @@ -package org.utplsql.sqldev.ui.runner - -import java.awt.Graphics -import java.awt.event.FocusEvent -import java.awt.event.FocusListener -import javax.swing.JTextField -import javax.swing.UIManager - -class RunnerTextField extends JTextField implements FocusListener { - - new() { - super() - this.addFocusListener = this - } - - override paintComponent(Graphics g) { - // default for non-opaque components - if (!opaque) { - super.paintComponent(g) - return - } - - g.color = UIManager.getColor("TextField.inactiveBackground") - g.fillRect(0, 0, width, height) - - // do rest, changing opaque to ensure background is not overwritten - setOpaque(false) - super.paintComponent(g) - setOpaque(true) - } - - override void focusGained(FocusEvent e) { - this.caret.visible = true - } - - override focusLost(FocusEvent e) { - this.caret.visible = false - } -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextPane.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextPane.java new file mode 100644 index 00000000..cd1fb700 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextPane.java @@ -0,0 +1,67 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.runner; + +import java.awt.Graphics; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; + +import javax.swing.JTextPane; +import javax.swing.UIManager; + +public class RunnerTextPane extends JTextPane implements FocusListener { + private static final long serialVersionUID = 1089473481444949272L; + + public RunnerTextPane() { + super(); + addFocusListener(this); + } + + @Override + public void paintComponent(final Graphics g) { + // default for non-opaque components + if (isOpaque()) { + super.paintComponent(g); + return; + } + + // use value of JTextField for consistency + g.setColor(UIManager.getColor("TextField.inactiveBackground")); + g.fillRect(0, 0, getWidth(), getHeight()); + setOpaque(false); + + // do rest, changing opaque to ensure background is not overwritten + super.paintComponent(g); + setOpaque(true); + } + + @Override + public void focusGained(final FocusEvent e) { + getCaret().setVisible(true); + } + + @Override + public void focusLost(final FocusEvent e) { + getCaret().setVisible(false); + } + + @Override + public void setText(final String t) { + super.setText(t); + // ensure left parts of long lines are always visible + setCaretPosition(0); + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextPane.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextPane.xtend deleted file mode 100644 index e3e4c663..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerTextPane.xtend +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.utplsql.sqldev.ui.runner - -import java.awt.Graphics -import java.awt.event.FocusEvent -import java.awt.event.FocusListener -import javax.swing.JTextPane -import javax.swing.UIManager - -class RunnerTextPane extends JTextPane implements FocusListener{ - - new() { - super() - this.addFocusListener = this - } - - override paintComponent(Graphics g) { - // default for non-opaque components - if (!opaque) { - super.paintComponent(g) - return - } - - // use value of JTextField for consistency - g.color = UIManager.getColor("TextField.inactiveBackground") - g.fillRect(0, 0, width, height) - - // do rest, changing opaque to ensure background is not overwritten - setOpaque(false) - super.paintComponent(g) - setOpaque(true) - } - - override void focusGained(FocusEvent e) { - this.caret.visible = true - } - - override focusLost(FocusEvent e) { - this.caret.visible = false - } - - override setText(String t) { - super.setText(t) - // ensure left parts of long lines are always visible - caretPosition = 0 - } -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerView.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerView.java new file mode 100644 index 00000000..e97d7ba0 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerView.java @@ -0,0 +1,54 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.runner; + +import java.awt.Component; + +import javax.swing.Icon; + +import org.utplsql.sqldev.resources.UtplsqlResources; + +import oracle.ide.docking.DockableWindow; +import oracle.ide.layout.ViewId; + +public class RunnerView extends DockableWindow { + private static final String VIEW_NAME = "UTPLSQL_RUNNER_VIEW"; + public static final ViewId VIEW_ID = new ViewId(RunnerFactory.FACTORY_NAME, VIEW_NAME); + private RunnerPanel panel; + + @Override + public String getTitleName() { + return UtplsqlResources.getString("RUNNER_VIEW_TITLE"); + } + + @Override + public Component getGUI() { + if (panel == null) { + panel = new RunnerPanel(); + } + return panel.getGUI(); + } + + @Override + public Icon getTabIcon() { + return UtplsqlResources.getIcon("UTPLSQL_ICON"); + } + + public RunnerPanel getRunnerPanel() { + getGUI(); + return panel; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerView.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerView.xtend deleted file mode 100644 index 49050d4a..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerView.xtend +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.ui.runner - -import oracle.ide.docking.DockableWindow -import oracle.ide.layout.ViewId -import org.utplsql.sqldev.resources.UtplsqlResources - -class RunnerView extends DockableWindow { - static val VIEW_NAME = "UTPLSQL_RUNNER_VIEW" - public static val ViewId VIEW_ID = new ViewId(RunnerFactory.FACTORY_NAME, VIEW_NAME) - var RunnerPanel panel - - override getTitleName() { - return UtplsqlResources.getString("RUNNER_VIEW_TITLE") - } - - override getGUI() { - if (panel === null) { - panel = new RunnerPanel - } - return panel.getGUI() - } - - override getTabIcon() { - return UtplsqlResources.getIcon("UTPLSQL_ICON") - } - - def getRunnerPanel() { - getGUI() - return panel - } -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/ScrollablePanel.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/ScrollablePanel.java new file mode 100644 index 00000000..b2398d6d --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/ScrollablePanel.java @@ -0,0 +1,55 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.runner; + +import java.awt.Dimension; +import java.awt.Rectangle; + +import javax.swing.JPanel; +import javax.swing.Scrollable; + +/** + * Fixes resizing issues of JTextArea when put into JPanel and JPanel into JScrollPane + * Solution is based on https://stackoverflow.com/questions/15783014/jtextarea-on-jpanel-inside-jscrollpane-does-not-resize-properly/15786939 + */ +public class ScrollablePanel extends JPanel implements Scrollable { + private static final long serialVersionUID = -8074226692678606351L; + + @Override + public Dimension getPreferredScrollableViewportSize() { + return super.getPreferredSize(); + } + + @Override + public int getScrollableUnitIncrement(final Rectangle visibleRect, final int orientation, final int direction) { + return 0; + } + + @Override + public int getScrollableBlockIncrement(final Rectangle visibleRect, final int orientation, final int direction) { + return 0; + } + + @Override + public boolean getScrollableTracksViewportWidth() { + return true; + } + + @Override + public boolean getScrollableTracksViewportHeight() { + return false; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/ScrollablePanel.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/ScrollablePanel.xtend deleted file mode 100644 index fb834008..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/ScrollablePanel.xtend +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.ui.runner - -import java.awt.Rectangle -import javax.swing.JPanel -import javax.swing.Scrollable - -/* - * Fixes resizing issues of JTextArea when put into JPanel and JPanel into JScrollPane - * Solution is based on https://stackoverflow.com/questions/15783014/jtextarea-on-jpanel-inside-jscrollpane-does-not-resize-properly/15786939 - */ -class ScrollablePanel extends JPanel implements Scrollable { - - override getPreferredScrollableViewportSize() { - return super.getPreferredSize() - } - - override getScrollableUnitIncrement(Rectangle visibleRect, int orientation, int direction) { - return 0 - } - - override getScrollableBlockIncrement(Rectangle visibleRect, int orientation, int direction) { - return 0 - } - - override getScrollableTracksViewportWidth() { - return true - } - - override getScrollableTracksViewportHeight() { - return false - } -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/SmartTime.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/SmartTime.java new file mode 100644 index 00000000..8e4e046b --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/SmartTime.java @@ -0,0 +1,71 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.runner; + +import java.text.DecimalFormat; + +public class SmartTime { + private Double seconds; + private boolean smart = false; + + public SmartTime() { + super(); + } + + public SmartTime(final Double seconds, final boolean smart) { + super(); + this.seconds = seconds; + this.smart = smart; + } + + public void setSeconds(final Double seconds) { + this.seconds = seconds; + } + + public void setSmart(final boolean smart) { + this.smart = smart; + } + + public Double getSeconds() { + return seconds; + } + + @Override + public String toString() { + String ret = null; + if (seconds == null) { + ret = null; + } else if (smart) { + if (seconds >= 60 * 60) { + final DecimalFormat formatter = new DecimalFormat("#0.00"); + ret = formatter.format(seconds / 60 / 60) + " h"; + } else if (seconds >= 60) { + final DecimalFormat formatter = new DecimalFormat("#0.00"); + ret = formatter.format(seconds / 60) + " min"; + } else if (seconds >= 1) { + final DecimalFormat formatter = new DecimalFormat("#0.000"); + ret = formatter.format(seconds) + " s"; + } else { + final DecimalFormat formatter = new DecimalFormat("##0"); + ret = formatter.format(seconds * 1000) + " ms"; + } + } else { + final DecimalFormat formatter = new DecimalFormat("##,##0.000"); + ret = formatter.format(seconds); + } + return ret; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/SmartTime.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/SmartTime.xtend deleted file mode 100644 index 6dc7246a..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/SmartTime.xtend +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.ui.runner - -import java.text.DecimalFormat - -class SmartTime { - var Double seconds - var boolean smart = false - - new() { - super() - } - - new(Double seconds, boolean smart) { - super() - this.seconds = seconds - this.smart = smart - } - - def setSeconds(Double seconds) { - this.seconds = seconds - } - - def setSmart(boolean smart) { - this.smart = smart - } - - def getSeconds() { - return seconds - } - - override toString() { - var String ret; - if (seconds === null) { - ret = null - } else if (smart) { - if (seconds >= 60*60) { - val DecimalFormat formatter = new DecimalFormat("#0.00") - ret = formatter.format(seconds / 60 / 60) + " h" - } else if (seconds >= 60) { - val DecimalFormat formatter = new DecimalFormat("#0.00") - ret = formatter.format(seconds / 60) + " min" - } else if (seconds >= 1) { - val DecimalFormat formatter = new DecimalFormat("#0.000") - ret = formatter.format(seconds) + " s" - } else { - val DecimalFormat formatter = new DecimalFormat("##0") - ret = formatter.format(seconds * 1000) + " ms" - } - - } else { - val DecimalFormat formatter = new DecimalFormat("##,##0.000") - ret = formatter.format(seconds) - } - return ret - } - -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/TestOverviewTableModel.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/TestOverviewTableModel.java new file mode 100644 index 00000000..02e85d81 --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/TestOverviewTableModel.java @@ -0,0 +1,170 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.runner; + +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.Map.Entry; + +import javax.swing.Icon; +import javax.swing.table.DefaultTableModel; + +import org.utplsql.sqldev.model.PrefixTools; +import org.utplsql.sqldev.model.runner.Test; +import org.utplsql.sqldev.resources.UtplsqlResources; + +public class TestOverviewTableModel extends DefaultTableModel { + private static final long serialVersionUID = -4087082648970132657L; + + private LinkedHashMap tests; + private String commonPrefix; + private boolean commonPrefixCalculated; + private boolean showDescription; + private boolean useSmartTimes; + + public TestOverviewTableModel() { + super(); + } + + private void calcCommonPrefix() { + if (!commonPrefixCalculated && tests != null && tests.size() > 0) { + commonPrefix = PrefixTools.commonPrefix(new ArrayList(tests.keySet())); + fireTableDataChanged(); + commonPrefixCalculated = true; + } + } + + public void setModel(final LinkedHashMap tests, final boolean showDescription, + final boolean useSmartTimes) { + commonPrefixCalculated = false; + this.tests = tests; + this.showDescription = showDescription; + this.useSmartTimes = useSmartTimes; + calcCommonPrefix(); + fireTableDataChanged(); + } + + public void updateModel(final boolean showDescription) { + this.showDescription = showDescription; + fireTableDataChanged(); + } + + public CharSequence getTestIdColumnName() { + StringBuilder sb = new StringBuilder(); + calcCommonPrefix(); + if (commonPrefix == null || commonPrefix.isEmpty()) { + if (showDescription) { + sb.append(UtplsqlResources.getString("RUNNER_DESCRIPTION_LABEL")); + } else { + sb.append(UtplsqlResources.getString("RUNNER_TEST_ID_COLUMN")); + } + } else { + if (showDescription) { + sb.append(UtplsqlResources.getString("RUNNER_DESCRIPTION_LABEL")); + sb.append(" ("); + sb.append(commonPrefix); + sb.append(")"); + } else { + sb.append(commonPrefix); + } + } + return sb.toString(); + } + + public String getTimeColumnName() { + return UtplsqlResources.getString("RUNNER_TEST_EXECUTION_TIME_COLUMN") + (!useSmartTimes ? " [s]" : ""); + } + + public Test getTest(final int row) { + return new ArrayList>(tests.entrySet()).get(row).getValue(); + } + + @Override + public int getRowCount() { + if (tests == null) { + return 0; + } + return tests.size(); + } + + @Override + public int getColumnCount() { + return 5; + } + + @Override + public Object getValueAt(final int row, final int col) { + final Test test = getTest(row); + switch (col) { + case 0: + return test.getStatusIcon(); + case 1: + return test.getWarningIcon(); + case 2: + return test.getInfoIcon(); + case 3: + if (showDescription && test.getDescription() != null) { + return test.getDescription(); + } else { + return test.getId().substring(commonPrefix == null ? 0 : commonPrefix.length()); + } + case 4: + return test.getExecutionTime(); + default: + return null; + } + } + + @Override + public String getColumnName(final int col) { + switch (col) { + case 0: + case 1: + case 2: + return ""; // icons are used instead of descriptions + case 3: + return UtplsqlResources.getString(showDescription ? "RUNNER_DESCRIPTION_LABEL" : "RUNNER_TEST_ID_COLUMN"); + case 4: + return getTimeColumnName(); + default: + return null; + } + + } + + @Override + public boolean isCellEditable(final int row, final int column) { + return false; + } + + @Override + public Class getColumnClass(final int col) { + switch (col) { + case 0: + return Icon.class; + case 1: + return Icon.class; + case 2: + return Icon.class; + case 3: + return String.class; + case 4: + return Double.class; + default: + return String.class; + } + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/TestOverviewTableModel.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/TestOverviewTableModel.xtend deleted file mode 100644 index c0f021c2..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/TestOverviewTableModel.xtend +++ /dev/null @@ -1,159 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.utplsql.sqldev.ui.runner - -import java.util.LinkedHashMap -import javax.swing.Icon -import javax.swing.table.DefaultTableModel -import org.utplsql.sqldev.model.PrefixTools -import org.utplsql.sqldev.model.runner.Test -import org.utplsql.sqldev.resources.UtplsqlResources - -class TestOverviewTableModel extends DefaultTableModel { - LinkedHashMap tests - String commonPrefix - boolean commonPrefixCalculated - boolean showDescription - boolean useSmartTimes - - new() { - super() - } - - private def calcCommonPrefix() { - if (!commonPrefixCalculated && tests !== null && tests.size > 0) { - this.commonPrefix = PrefixTools.commonPrefix(tests.keySet.toList) - fireTableDataChanged() - commonPrefixCalculated = true - } - } - - def setModel(LinkedHashMap tests, boolean showDescription, boolean useSmartTimes) { - commonPrefixCalculated = false - this.tests = tests - this.showDescription = showDescription - this.useSmartTimes = useSmartTimes - calcCommonPrefix - fireTableDataChanged() - } - - def updateModel(boolean showDescription) { - this.showDescription = showDescription - fireTableDataChanged() - } - - def getTestIdColumnName() { - calcCommonPrefix - if (commonPrefix === null || commonPrefix == "") { - if (showDescription) { - UtplsqlResources.getString("RUNNER_DESCRIPTION_LABEL") - } else { - UtplsqlResources.getString("RUNNER_TEST_ID_COLUMN") - } - } else { - if (showDescription) { - '''«UtplsqlResources.getString("RUNNER_DESCRIPTION_LABEL")» («commonPrefix»)''' - } else { - commonPrefix - } - } - } - - def getTimeColumnName() { - val timeColumnName = '''«UtplsqlResources.getString("RUNNER_TEST_EXECUTION_TIME_COLUMN")»«IF !useSmartTimes» [s]«ENDIF»''' - return timeColumnName - } - - def getTest(int row) { - val entry = tests.entrySet.get(row) - val test = tests.get(entry.key) - return test - } - - override getRowCount() { - if (tests === null) { - return 0 - } - return tests.size() - } - - override getColumnCount() { - return 5 - } - - override getValueAt(int row, int col) { - val test = tests.entrySet.get(row).value - if (test === null) { - return null - } - switch (col) { - case 0: { - return test.statusIcon - } - case 1: { - return test.warningIcon - } - case 2: { - return test.infoIcon - } - case 3: { - return if(showDescription && test.description !== null) { - test.description - } else { - test.id.substring(if(commonPrefix === null) {0} else {commonPrefix.length}) - } - } - case 4: { - return test.executionTime - } - default: { - return null - } - } - } - - override getColumnName(int col) { - return #["", "", "", UtplsqlResources.getString(if (showDescription) {"RUNNER_DESCRIPTION_LABEL"} else {"RUNNER_TEST_ID_COLUMN"}), - timeColumnName].get(col) - } - - override isCellEditable(int row, int column) { - return false - } - - override getColumnClass(int col) { - switch (col) { - case 0: { - return Icon - } - case 1: { - return Icon - } - case 2: { - return Icon - } - case 3: { - return String - } - case 4: { - return Double - } - default: { - return String - } - } - } -} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/WrapLayout.java b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/WrapLayout.java new file mode 100644 index 00000000..1ccf2f2c --- /dev/null +++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/WrapLayout.java @@ -0,0 +1,188 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.ui.runner; + +import java.awt.Component; +import java.awt.Container; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.Insets; + +import javax.swing.JScrollPane; +import javax.swing.SwingUtilities; + +/** + * FlowLayout subclass that fully supports wrapping of components. + * Converted to Xtend based on http://www.camick.com/java/source/WrapLayout.java + * Converted back to Java with small amendments to original code + */ +public class WrapLayout extends FlowLayout { + private static final long serialVersionUID = -4576022991725404247L; + + /** + * Constructs a new WrapLayout with a left alignment and a default + * 5-unit horizontal and vertical gap. + */ + public WrapLayout() { + super(); + } + + /** + * Constructs a new FlowLayout with the specified alignment and a + * default 5-unit horizontal and vertical gap. The value of the alignment + * argument must be one of WrapLayout, WrapLayout, or + * WrapLayout. + * + * @param align + * the alignment value + */ + public WrapLayout(final int align) { + super(align); + } + + /** + * Creates a new flow layout manager with the indicated alignment and the + * indicated horizontal and vertical gaps. + *

+ * The value of the alignment argument must be one of WrapLayout, + * WrapLayout, or WrapLayout. + * + * @param align + * the alignment value + * @param hgap + * the horizontal gap between components + * @param vgap + * the vertical gap between components + */ + public WrapLayout(final int align, final int hgap, final int vgap) { + super(align, hgap, vgap); + } + + /** + * Returns the preferred dimensions for this layout given the visible + * components in the specified target container. + * + * @param target + * the component which needs to be laid out + * @return the preferred dimensions to lay out the subcomponents of the + * specified container + */ + @Override + public Dimension preferredLayoutSize(final Container target) { + return layoutSize(target, true); + } + + /** + * Returns the minimum dimensions needed to layout the visible components + * contained in the specified target container. + * + * @param target + * the component which needs to be laid out + * @return the minimum dimensions to lay out the subcomponents of the specified + * container + */ + @Override + public Dimension minimumLayoutSize(final Container target) { + Dimension minimum = layoutSize(target, false); + minimum.width -= (getHgap() + 1); + return minimum; + } + + /** + * Returns the minimum or preferred dimension needed to layout the target + * container. + * + * @param target + * target to get layout size for + * @param preferred + * should preferred size be calculated + * @return the dimension to layout the target container + */ + private Dimension layoutSize(final Container target, final boolean preferred) { + synchronized (target.getTreeLock()) { + // Each row must fit with the width allocated to the container. + // When the container width = 0, the preferred width of the container + // has not yet been calculated so lets ask for the maximum. + Container container = target; + while (container.getSize().width == 0 && container.getParent() != null) { + container = container.getParent(); + } + int targetWidth = container.getSize().width; + if (targetWidth == 0) { + targetWidth = Integer.MAX_VALUE; + } + int hgap = getHgap(); + int vgap = getVgap(); + Insets insets = target.getInsets(); + int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2); + int maxWidth = targetWidth - horizontalInsetsAndGap; + // Fit components into the allowed width + Dimension dim = new Dimension(0, 0); + int rowWidth = 0; + int rowHeight = 0; + int nmembers = target.getComponentCount(); + for (int i = 0; i < nmembers; i++) { + Component m = target.getComponent(i); + if (m.isVisible()) { + Dimension d = preferred ? m.getPreferredSize() : m.getMinimumSize(); + // Can't add the component to current row. Start a new row. + if (rowWidth + d.width > maxWidth) { + addRow(dim, rowWidth, rowHeight); + rowWidth = 0; + rowHeight = 0; + } + // Add a horizontal gap for all components after the first + if (rowWidth != 0) { + rowWidth += hgap; + } + rowWidth += d.width; + rowHeight = Math.max(rowHeight, d.height); + } + } + addRow(dim, rowWidth, rowHeight); + dim.width += horizontalInsetsAndGap; + dim.height += insets.top + insets.bottom + vgap * 2; + // When using a scroll pane or the DecoratedLookAndFeel we need to + // make sure the preferred size is less than the size of the + // target container so shrinking the container size works + // correctly. Removing the horizontal gap is an easy way to do this. + Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane.class, target); + if (scrollPane != null && target.isValid()) { + dim.width -= (hgap + 1); + } + return dim; + } + } + + /** + * A new row has been completed. Use the dimensions of this row to update the + * preferred size for the container. + * + * @param dim + * update the width and height when appropriate + * @param rowWidth + * the width of the row to add + * @param rowHeight + * the height of the row to add + */ + private void addRow(final Dimension dim, final int rowWidth, final int rowHeight) { + dim.width = Math.max(dim.width, rowWidth); + if (dim.height > 0) { + dim.height += getVgap(); + } + dim.height += rowHeight; + } +} diff --git a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/WrapLayout.xtend b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/WrapLayout.xtend deleted file mode 100644 index dce4695c..00000000 --- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/WrapLayout.xtend +++ /dev/null @@ -1,168 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.ui.runner - -import java.awt.Component -import java.awt.Container -import java.awt.Dimension -import java.awt.FlowLayout -import java.awt.Insets -import javax.swing.JScrollPane -import javax.swing.SwingUtilities - -/** - * FlowLayout subclass that fully supports wrapping of components. - * Converted to Xtend based on http://www.camick.com/java/source/WrapLayout.java - */ -class WrapLayout extends FlowLayout { - - /** - * Constructs a new WrapLayout with a left - * alignment and a default 5-unit horizontal and vertical gap. - */ - new() { - super() - } - - /** - * Constructs a new FlowLayout with the specified - * alignment and a default 5-unit horizontal and vertical gap. - * The value of the alignment argument must be one of - * WrapLayout, WrapLayout, - * or WrapLayout. - * @param align the alignment value - */ - new(int align) { - super(align) - } - - /** - * Creates a new flow layout manager with the indicated alignment - * and the indicated horizontal and vertical gaps. - *

- * The value of the alignment argument must be one of - * WrapLayout, WrapLayout, - * or WrapLayout. - * @param align the alignment value - * @param hgap the horizontal gap between components - * @param vgap the vertical gap between components - */ - new(int align, int hgap, int vgap) { - super(align, hgap, vgap) - } - - /** - * Returns the preferred dimensions for this layout given the - * visible components in the specified target container. - * @param target the component which needs to be laid out - * @return the preferred dimensions to lay out the - * subcomponents of the specified container - */ - override Dimension preferredLayoutSize(Container target) { - return layoutSize(target, true) - } - - /** - * Returns the minimum dimensions needed to layout the visible - * components contained in the specified target container. - * @param target the component which needs to be laid out - * @return the minimum dimensions to lay out the - * subcomponents of the specified container - */ - override Dimension minimumLayoutSize(Container target) { - var Dimension minimum = layoutSize(target, false) - minimum.width -= (getHgap() + 1) - return minimum - } - - /** - * Returns the minimum or preferred dimension needed to layout the target - * container. - * @param target target to get layout size for - * @param preferred should preferred size be calculated - * @return the dimension to layout the target container - */ - def private Dimension layoutSize(Container target, boolean preferred) { - synchronized (target.getTreeLock()) { - // Each row must fit with the width allocated to the containter. - // When the container width = 0, the preferred width of the container - // has not yet been calculated so lets ask for the maximum. - var int targetWidth = target.getSize().width - var Container container = target - while (container.getSize().width === 0 && container.getParent() !== null) { - container = container.getParent() - } - targetWidth = container.getSize().width - if(targetWidth === 0) targetWidth = Integer.MAX_VALUE - var int hgap = getHgap() - var int vgap = getVgap() - var Insets insets = target.getInsets() - var int horizontalInsetsAndGap = insets.left + insets.right + (hgap * 2) - var int maxWidth = targetWidth - horizontalInsetsAndGap - // Fit components into the allowed width - var Dimension dim = new Dimension(0, 0) - var int rowWidth = 0 - var int rowHeight = 0 - var int nmembers = target.getComponentCount() - for (var int i = 0; i < nmembers; i++) { - var Component m = target.getComponent(i) - if (m.isVisible()) { - var Dimension d = if(preferred) m.getPreferredSize() else m.getMinimumSize() - // Can't add the component to current row. Start a new row. - if (rowWidth + d.width > maxWidth) { - addRow(dim, rowWidth, rowHeight) - rowWidth = 0 - rowHeight = 0 - } - // Add a horizontal gap for all components after the first - if (rowWidth !== 0) { - rowWidth += hgap - } - rowWidth += d.width - rowHeight = Math.max(rowHeight, d.height) - } - } - addRow(dim, rowWidth, rowHeight) - dim.width += horizontalInsetsAndGap - dim.height += insets.top + insets.bottom + vgap * 2 - // When using a scroll pane or the DecoratedLookAndFeel we need to - // make sure the preferred size is less than the size of the - // target containter so shrinking the container size works - // correctly. Removing the horizontal gap is an easy way to do this. - var Container scrollPane = SwingUtilities.getAncestorOfClass(JScrollPane, target) - if (scrollPane !== null && target.isValid()) { - dim.width -= (hgap + 1) - } - return dim - } - } - - /* - * A new row has been completed. Use the dimensions of this row - * to update the preferred size for the container. - * - * @param dim update the width and height when appropriate - * @param rowWidth the width of the row to add - * @param rowHeight the height of the row to add - */ - def private void addRow(Dimension dim, int rowWidth, int rowHeight) { - dim.width = Math.max(dim.width, rowWidth) - if (dim.height > 0) { - dim.height += getVgap() - } - dim.height += rowHeight - } -} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/AbstractJdbcTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/AbstractJdbcTest.java new file mode 100644 index 00000000..c0f16809 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/AbstractJdbcTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test; + +import java.io.IOException; +import java.io.StringReader; +import java.util.ArrayList; +import java.util.List; +import java.util.Properties; + +import org.springframework.jdbc.BadSqlGrammarException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.jdbc.datasource.SingleConnectionDataSource; +import org.utplsql.sqldev.exception.GenericRuntimeException; + +import oracle.dbtools.raptor.newscriptrunner.ISQLCommand; +import oracle.dbtools.raptor.newscriptrunner.SQLCommand; +import oracle.dbtools.worksheet.scriptparser.sqlplus.SQLPlusScriptParser; + +public abstract class AbstractJdbcTest { + protected static SingleConnectionDataSource dataSource; + protected static JdbcTemplate jdbcTemplate; + protected static SingleConnectionDataSource sysDataSource; + protected static JdbcTemplate sysJdbcTemplate; + + static { + final Properties p = new Properties(); + try { + p.load(AbstractJdbcTest.class.getClass().getResourceAsStream("/test.properties")); + } catch (IOException e) { + throw new GenericRuntimeException("Cannot read test.properties", e); + } + // create dataSource and jdbcTemplate + dataSource = new SingleConnectionDataSource(); + dataSource.setDriverClassName("oracle.jdbc.OracleDriver"); + dataSource.setUrl("jdbc:oracle:thin:@" + p.getProperty("host") + ":" + p.getProperty("port") + "/" + + p.getProperty("service")); + dataSource.setUsername(p.getProperty("scott_username")); + dataSource.setPassword(p.getProperty("scott_password")); + jdbcTemplate = new JdbcTemplate(dataSource); + // create dbaDataSource and dbaJdbcTemplate + sysDataSource = new SingleConnectionDataSource(); + sysDataSource.setDriverClassName("oracle.jdbc.OracleDriver"); + sysDataSource.setUrl("jdbc:oracle:thin:@" + p.getProperty("host") + ":" + p.getProperty("port") + "/" + + p.getProperty("service")); + sysDataSource.setUsername(p.getProperty("sys_username")); + sysDataSource.setPassword(p.getProperty("sys_password")); + sysJdbcTemplate = new JdbcTemplate(sysDataSource); + } + + public static List getStatements(final String sqlplusScript) { + SQLPlusScriptParser p = new SQLPlusScriptParser(new StringReader(sqlplusScript)); + final ArrayList stmts = new ArrayList<>(); + while (p.hasNext()) { + final ISQLCommand stmt = p.next(); + if ((stmt.getExecutable() || stmt.getRunnable()) && stmt.getStmtType() != SQLCommand.StmtType.G_C_COMMENT + && stmt.getStmtType() != SQLCommand.StmtType.G_C_MULTILINECOMMENT + && stmt.getStmtType() != SQLCommand.StmtType.G_C_SQLPLUS) { + stmts.add(stmt.getSql()); + } + } + return stmts; + } + + public static void executeAndIgnore(JdbcTemplate template, String sql) { + try { + template.execute(sql); + } catch (BadSqlGrammarException e) { + // ignore + } + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/AbstractJdbcTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/AbstractJdbcTest.xtend deleted file mode 100644 index 015885e0..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/AbstractJdbcTest.xtend +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test - -import java.io.StringReader -import java.util.ArrayList -import java.util.Properties -import oracle.dbtools.raptor.newscriptrunner.SQLCommand.StmtType -import oracle.dbtools.worksheet.scriptparser.sqlplus.SQLPlusScriptParser -import org.springframework.jdbc.core.JdbcTemplate -import org.springframework.jdbc.datasource.SingleConnectionDataSource - -abstract class AbstractJdbcTest { - protected static var SingleConnectionDataSource dataSource - protected static var JdbcTemplate jdbcTemplate - protected static var SingleConnectionDataSource sysDataSource - protected static var JdbcTemplate sysJdbcTemplate - // static initializer not supported in Xtend, see https://bugs.eclipse.org/bugs/show_bug.cgi?id=429141 - protected static val _staticInitializerForDataSourceAndJdbcTemplate = { - val p = new Properties() - p.load(AbstractJdbcTest.getClass().getResourceAsStream("/test.properties")) - // create dataSource and jdbcTemplate - dataSource = new SingleConnectionDataSource() - dataSource.driverClassName = "oracle.jdbc.OracleDriver" - dataSource.url = '''jdbc:oracle:thin:@«p.getProperty("host")»:«p.getProperty("port")»/«p.getProperty("service")»''' - dataSource.username = p.getProperty("scott_username") - dataSource.password = p.getProperty("scott_password") - jdbcTemplate = new JdbcTemplate(dataSource) - // create dbaDataSource and dbaJdbcTemplate - sysDataSource = new SingleConnectionDataSource() - sysDataSource.driverClassName = "oracle.jdbc.OracleDriver" - sysDataSource.url = '''jdbc:oracle:thin:@«p.getProperty("host")»:«p.getProperty("port")»/«p.getProperty("service")»''' - sysDataSource.username = p.getProperty("sys_username") - sysDataSource.password = p.getProperty("sys_password") - sysJdbcTemplate = new JdbcTemplate(AbstractJdbcTest.sysDataSource) - } - - def static getStatements(String sqlplusScript) { - var SQLPlusScriptParser p = new SQLPlusScriptParser(new StringReader(sqlplusScript)) - val stmts = new ArrayList - while (p.hasNext) { - val stmt = p.next - if ((stmt.executable || stmt.runnable) && stmt.stmtType != StmtType.G_C_COMMENT && - stmt.stmtType != StmtType.G_C_MULTILINECOMMENT && stmt.stmtType != StmtType.G_C_SQLPLUS) { - stmts.add(stmt.sql) - } - } - return stmts; - } -} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/JsonToStringStylerTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/JsonToStringStylerTest.java new file mode 100644 index 00000000..2a0433cb --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/JsonToStringStylerTest.java @@ -0,0 +1,196 @@ +/* + * Copyright 2020 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test; + +import java.util.Arrays; + +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.model.runner.Run; +import org.utplsql.sqldev.model.ut.OutputLines; + +public class JsonToStringStylerTest { + + @Test + public void outputLinesWithTreeLines() { + final OutputLines o = new OutputLines(); + o.setLines(new String[] { "line \"1\"", "line2", "line3" }); + o.setNumlines(3); + + final String actual = o.toString(); + + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + sb.append(" \"className\": \"OutputLines\",\n"); + sb.append(" \"lines\": [\n"); + sb.append(" \"line \\\"1\\\"\",\n"); + sb.append(" \"line2\",\n"); + sb.append(" \"line3\"\n"); + sb.append(" ],\n"); + sb.append(" \"numlines\": 3\n"); + sb.append("}"); + Assert.assertEquals(sb.toString(), actual); + } + + @Test + public void outputLinesWithoutLines() { + final OutputLines o = new OutputLines(); + o.setLines(new String[] {}); + o.setNumlines(0); + + final String actual = o.toString(); + + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + sb.append(" \"className\": \"OutputLines\",\n"); + sb.append(" \"lines\": [],\n"); + sb.append(" \"numlines\": 0\n"); + sb.append("}"); + Assert.assertEquals(sb.toString(), actual); + } + + @Test + public void emptyRun() { + final Run r = new Run("1", "MyConnection", Arrays.asList()); + + final String actual = r.toString(); + + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + sb.append(" \"className\": \"Run\",\n"); + sb.append(" \"reporterId\": \"1\",\n"); + sb.append(" \"connectionName\": \"MyConnection\",\n"); + sb.append(" \"pathList\": [],\n"); + sb.append(" \"currentTestNumber\": null,\n"); + sb.append(" \"currentTest\": null,\n"); + sb.append(" \"totalNumberOfTests\": null,\n"); + sb.append(" \"startTime\": null,\n"); + sb.append(" \"endTime\": null,\n"); + sb.append(" \"executionTime\": null,\n"); + sb.append(" \"counter\": {\n"); + sb.append(" \"className\": \"Counter\",\n"); + sb.append(" \"disabled\": null,\n"); + sb.append(" \"success\": null,\n"); + sb.append(" \"failure\": null,\n"); + sb.append(" \"error\": null,\n"); + sb.append(" \"warning\": null\n"); + sb.append(" },\n"); + sb.append(" \"infoCount\": null,\n"); + sb.append(" \"errorStack\": null,\n"); + sb.append(" \"serverOutput\": null,\n"); + sb.append(" \"tests\": [],\n"); + sb.append(" \"status\": null,\n"); + sb.append(" \"start\": null,\n"); + sb.append(" \"endTime\": null,\n"); + sb.append(" \"totalNumberOfCompletedTests\": -1\n"); + sb.append("}"); + Assert.assertEquals(sb.toString(), actual.toString()); + } + + + @Test + public void runWithTests() { + final Run r = new Run("1", "MyConnection", Arrays.asList()); + final org.utplsql.sqldev.model.runner.Test t1 = new org.utplsql.sqldev.model.runner.Test(); + t1.setId("1"); + t1.setName("Test One"); + r.getTests().put(t1.getId(), t1); + final org.utplsql.sqldev.model.runner.Test t2 = new org.utplsql.sqldev.model.runner.Test(); + t2.setId("2"); + t2.setName("Test Two"); + r.getTests().put(t2.getId(), t2); + + final String actual = r.toString(); + + final StringBuilder sb = new StringBuilder(); + sb.append("{\n"); + sb.append(" \"className\": \"Run\",\n"); + sb.append(" \"reporterId\": \"1\",\n"); + sb.append(" \"connectionName\": \"MyConnection\",\n"); + sb.append(" \"pathList\": [],\n"); + sb.append(" \"currentTestNumber\": null,\n"); + sb.append(" \"currentTest\": null,\n"); + sb.append(" \"totalNumberOfTests\": null,\n"); + sb.append(" \"startTime\": null,\n"); + sb.append(" \"endTime\": null,\n"); + sb.append(" \"executionTime\": null,\n"); + sb.append(" \"counter\": {\n"); + sb.append(" \"className\": \"Counter\",\n"); + sb.append(" \"disabled\": null,\n"); + sb.append(" \"success\": null,\n"); + sb.append(" \"failure\": null,\n"); + sb.append(" \"error\": null,\n"); + sb.append(" \"warning\": null\n"); + sb.append(" },\n"); + sb.append(" \"infoCount\": null,\n"); + sb.append(" \"errorStack\": null,\n"); + sb.append(" \"serverOutput\": null,\n"); + sb.append(" \"tests\": [\n"); + sb.append(" {\n"); + sb.append(" \"className\": \"Test\",\n"); + sb.append(" \"id\": \"1\",\n"); + sb.append(" \"startTime\": null,\n"); + sb.append(" \"endTime\": null,\n"); + sb.append(" \"executionTime\": null,\n"); + sb.append(" \"counter\": null,\n"); + sb.append(" \"errorStack\": null,\n"); + sb.append(" \"serverOutput\": null,\n"); + sb.append(" \"warnings\": null,\n"); + sb.append(" \"executableType\": null,\n"); + sb.append(" \"ownerName\": null,\n"); + sb.append(" \"objectName\": null,\n"); + sb.append(" \"procedureName\": null,\n"); + sb.append(" \"disabled\": null,\n"); + sb.append(" \"name\": \"Test One\",\n"); + sb.append(" \"description\": null,\n"); + sb.append(" \"testNumber\": null,\n"); + sb.append(" \"failedExpectations\": null,\n"); + sb.append(" \"statusIcon\": null,\n"); + sb.append(" \"warningIcon\": null,\n"); + sb.append(" \"infoIcon\": null\n"); + sb.append(" },\n"); + sb.append(" {\n"); + sb.append(" \"className\": \"Test\",\n"); + sb.append(" \"id\": \"2\",\n"); + sb.append(" \"startTime\": null,\n"); + sb.append(" \"endTime\": null,\n"); + sb.append(" \"executionTime\": null,\n"); + sb.append(" \"counter\": null,\n"); + sb.append(" \"errorStack\": null,\n"); + sb.append(" \"serverOutput\": null,\n"); + sb.append(" \"warnings\": null,\n"); + sb.append(" \"executableType\": null,\n"); + sb.append(" \"ownerName\": null,\n"); + sb.append(" \"objectName\": null,\n"); + sb.append(" \"procedureName\": null,\n"); + sb.append(" \"disabled\": null,\n"); + sb.append(" \"name\": \"Test Two\",\n"); + sb.append(" \"description\": null,\n"); + sb.append(" \"testNumber\": null,\n"); + sb.append(" \"failedExpectations\": null,\n"); + sb.append(" \"statusIcon\": null,\n"); + sb.append(" \"warningIcon\": null,\n"); + sb.append(" \"infoIcon\": null\n"); + sb.append(" }\n"); + sb.append(" ],\n"); + sb.append(" \"status\": null,\n"); + sb.append(" \"start\": null,\n"); + sb.append(" \"endTime\": null,\n"); + sb.append(" \"totalNumberOfCompletedTests\": -1\n"); + sb.append("}"); + Assert.assertEquals(sb.toString(), actual.toString()); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/PrefixToolsTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/PrefixToolsTest.java new file mode 100644 index 00000000..c4b0983e --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/PrefixToolsTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test; + +import java.util.Arrays; + +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.model.PrefixTools; + +public class PrefixToolsTest { + @Test + public void two() { + final String actual = PrefixTools.commonPrefix(Arrays.asList("junit.test.a", "junit.test.b")); + final String expected = "junit.test."; + Assert.assertEquals(expected, actual); + } + + @Test + public void oneWithDot() { + final String actual = PrefixTools.commonPrefix(Arrays.asList("junit.test.a")); + final String expected = "junit.test."; + Assert.assertEquals(expected, actual); + } + + @Test + public void oneWithoutDot() { + final String actual = PrefixTools.commonPrefix(Arrays.asList("junit-test-a")); + final String expected = ""; + Assert.assertEquals(expected, actual); + } + + @Test + public void twoOverlapLeft() { + final String actual = PrefixTools.commonPrefix(Arrays.asList("a.b.c", "a.b.c.d")); + final String expected = ""; + Assert.assertEquals(expected, actual); + } + + @Test + public void twoOverlapRight() { + final String actual = PrefixTools.commonPrefix(Arrays.asList("a.b.c.d", "a.b.c")); + final String expected = ""; + Assert.assertEquals(expected, actual); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/PrefixToolsTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/PrefixToolsTest.xtend deleted file mode 100644 index 8c7d7760..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/PrefixToolsTest.xtend +++ /dev/null @@ -1,43 +0,0 @@ -package org.utplsql.sqldev.test - -import org.junit.Assert -import org.junit.Test -import org.utplsql.sqldev.model.PrefixTools - -class PrefixToolsTest { - @Test - def void two() { - val actual = PrefixTools.commonPrefix(#["junit.test.a", "junit.test.b"]) - val expected = "junit.test." - Assert.assertEquals(expected, actual) - } - - @Test - def void oneWithDot() { - val actual = PrefixTools.commonPrefix(#["junit.test.a"]) - val expected = "junit.test." - Assert.assertEquals(expected, actual) - } - - @Test - def void oneWithoutDot() { - val actual = PrefixTools.commonPrefix(#["junit-test-a"]) - val expected = "" - Assert.assertEquals(expected, actual) - } - - @Test - def void twoOverlapLeft() { - val actual = PrefixTools.commonPrefix(#["a.b.c", "a.b.c.d"]) - val expected = "" - Assert.assertEquals(expected, actual) - } - - @Test - def void twoOverlapRight() { - val actual = PrefixTools.commonPrefix(#["a.b.c.d", "a.b.c"]) - val expected = "" - Assert.assertEquals(expected, actual) - } - -} \ No newline at end of file diff --git a/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterEventConsumer.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/ResourceTest.java similarity index 62% rename from sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterEventConsumer.xtend rename to sqldev/src/test/java/org/utplsql/sqldev/test/ResourceTest.java index 9a1d7727..4c6c39e4 100644 --- a/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterEventConsumer.xtend +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/ResourceTest.java @@ -13,12 +13,18 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.utplsql.sqldev.dal +package org.utplsql.sqldev.test; -import org.utplsql.sqldev.model.runner.RealtimeReporterEvent +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.resources.UtplsqlResources; -interface RealtimeReporterEventConsumer { - - def void process(RealtimeReporterEvent event) +public class ResourceTest { + @Test + public void windowPathsLabel() { + final String actual = UtplsqlResources.getString("WINDOW_PATHS_LABEL"); + final String expected = "utPLSQL paths"; + Assert.assertEquals(expected, actual); + } } diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/ResourceTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/ResourceTest.xtend deleted file mode 100644 index d283cb5d..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/ResourceTest.xtend +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test - -import org.junit.Assert -import org.junit.Test -import org.utplsql.sqldev.resources.UtplsqlResources - -class ResourceTest { - - @Test - def void windowPathsLabel() { - val actual = UtplsqlResources.getString("WINDOW_PATHS_LABEL") - val expected = "utPLSQL paths" - Assert.assertEquals(expected, actual) - } -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/SnippetTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/SnippetTest.java new file mode 100644 index 00000000..b3ebeb49 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/SnippetTest.java @@ -0,0 +1,87 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test; + +import java.io.File; +import java.nio.file.Paths; + +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.model.FileTools; +import org.utplsql.sqldev.snippet.SnippetMerger; + +public class SnippetTest { + private static final File USER_SNIPPETS_FILE = new File( + System.getProperty("user.home") + File.separator + "UserSnippets.xml"); + + @Test + public void mergeAsCopy() { + USER_SNIPPETS_FILE.delete(); + final SnippetMerger merger = new SnippetMerger(USER_SNIPPETS_FILE); + merger.merge(); + Assert.assertTrue(USER_SNIPPETS_FILE.exists()); + final String userSnippetsXml = new String(FileTools.readFile(USER_SNIPPETS_FILE.toPath())); + Assert.assertEquals(merger.getTemplate(), userSnippetsXml); + } + + @Test + public void mergeKeepExisting() { + USER_SNIPPETS_FILE.delete(); + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + sb.append("\n"); + sb.append(" \n"); + sb.append(" \n"); + sb.append(" \n"); + sb.append(" \n"); + sb.append(" \n"); + sb.append(" \n"); + sb.append(" \n"); + sb.append("\n"); + final String userSnippetsXml = sb.toString(); + FileTools.writeFile(Paths.get(USER_SNIPPETS_FILE.getAbsolutePath()), userSnippetsXml.getBytes()); + final SnippetMerger merger = new SnippetMerger(USER_SNIPPETS_FILE); + merger.merge(); + Assert.assertTrue(USER_SNIPPETS_FILE.exists()); + final String userSnippetsXml2 = new String(FileTools.readFile(USER_SNIPPETS_FILE.toPath())); + Assert.assertTrue(userSnippetsXml2.length() > userSnippetsXml.length()); + Assert.assertTrue(userSnippetsXml2.contains("")); + Assert.assertTrue(userSnippetsXml2.contains("")); + Assert.assertTrue(userSnippetsXml2.contains("")); + } + + @Test + public void mergeRemoveExisting() { + USER_SNIPPETS_FILE.delete(); + StringBuilder sb = new StringBuilder(); + sb.append("\n"); + sb.append("\n"); + sb.append(" \n"); + sb.append(" \n"); + sb.append("\n"); + final String userSnippetsXml = sb.toString(); + FileTools.writeFile(Paths.get(USER_SNIPPETS_FILE.getAbsolutePath()), userSnippetsXml.getBytes()); + final SnippetMerger merger = new SnippetMerger(USER_SNIPPETS_FILE); + merger.merge(); + Assert.assertTrue(USER_SNIPPETS_FILE.exists()); + final String userSnippetsXml2 = new String(FileTools.readFile(Paths.get(USER_SNIPPETS_FILE.getAbsolutePath()))); + Assert.assertTrue(userSnippetsXml2.length() > userSnippetsXml.length()); + Assert.assertFalse(userSnippetsXml2.contains("")); + Assert.assertFalse(userSnippetsXml2.contains("")); + Assert.assertTrue(userSnippetsXml2.contains("")); + Assert.assertTrue(userSnippetsXml2.contains("")); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/SnippetTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/SnippetTest.xtend deleted file mode 100644 index 1a7e2387..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/SnippetTest.xtend +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test - -import java.io.File -import java.nio.file.Files -import java.nio.file.Paths -import org.junit.Assert -import org.junit.Test -import org.utplsql.sqldev.snippet.SnippetMerger - -class SnippetTest { - - @Test - def void mergeAsCopy() { - val file = new File(System.getProperty("user.home") + File.separator + "UserSnippets.xml") - file.delete - val merger = new SnippetMerger(file) - merger.merge - Assert.assertTrue(file.exists) - val userSnippetsXml = new String(Files.readAllBytes(Paths.get(file.absolutePath))) - Assert.assertEquals(merger.template, userSnippetsXml ) - } - - @Test - def void mergeKeepExisting() { - val file = new File(System.getProperty("user.home") + File.separator + "UserSnippets.xml") - file.delete - val userSnippetsXml = ''' - - - - - - - - - - - '''.toString - Files.write(Paths.get(file.absolutePath), userSnippetsXml.bytes) - val merger = new SnippetMerger(file) - merger.merge - Assert.assertTrue(file.exists) - val userSnippetsXml2 = new String(Files.readAllBytes(Paths.get(file.absolutePath))) - Assert.assertTrue(userSnippetsXml2.length > userSnippetsXml.length) - Assert.assertTrue(userSnippetsXml2.contains('''''')) - Assert.assertTrue(userSnippetsXml2.contains('''''')) - Assert.assertTrue(userSnippetsXml2.contains('''''')) - } - - @Test - def void mergeRemoveExisting() { - val file = new File(System.getProperty("user.home") + File.separator + "UserSnippets.xml") - file.delete - val userSnippetsXml = ''' - - - - - - '''.toString - Files.write(Paths.get(file.absolutePath), userSnippetsXml.bytes) - val merger = new SnippetMerger(file) - merger.merge - Assert.assertTrue(file.exists) - val userSnippetsXml2 = new String(Files.readAllBytes(Paths.get(file.absolutePath))) - Assert.assertTrue(userSnippetsXml2.length > userSnippetsXml.length) - Assert.assertFalse(userSnippetsXml2.contains('''''')) - Assert.assertFalse(userSnippetsXml2.contains('''''')) - Assert.assertTrue(userSnippetsXml2.contains('''''')) - Assert.assertTrue(userSnippetsXml2.contains('''''')) - } - - -} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/StringToolsTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/StringToolsTest.java new file mode 100644 index 00000000..8cb642ca --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/StringToolsTest.java @@ -0,0 +1,57 @@ +/* + * Copyright 2020 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.model.StringTools; + +public class StringToolsTest { + + @Test + public void one_entry_as_CSV() { + final List list = new ArrayList<>(); + list.add("hello"); + Assert.assertEquals(" 'hello'\n", StringTools.getCSV(list, 5)); + } + + @Test + public void two_entries_as_CSV() { + final List list = new ArrayList<>(); + list.add("hello"); + list.add("world"); + Assert.assertEquals(" 'hello',\n 'world'\n", StringTools.getCSV(list, 5)); + } + + @Test + public void one_entry_as_simpleCSV() { + final List list = new ArrayList<>(); + list.add("hello"); + Assert.assertEquals("hello", StringTools.getSimpleCSV(list)); + } + + @Test + public void two_entries_as_simpleCSV() { + final List list = new ArrayList<>(); + list.add("hello"); + list.add("world"); + Assert.assertEquals("hello, world", StringTools.getSimpleCSV(list)); + } + +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/UrlToolsTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/UrlToolsTest.java new file mode 100644 index 00000000..acccfe16 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/UrlToolsTest.java @@ -0,0 +1,44 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test; + +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.model.URLTools; + +public class UrlToolsTest { + + @Test + public void replacePlusSign() { + Assert.assertEquals("+", URLTools.replaceHexChars("%2B")); + Assert.assertEquals("++", URLTools.replaceHexChars("%2B%2B")); + Assert.assertEquals("abc+%xyz", URLTools.replaceHexChars("abc%2B%xyz")); + } + + @Test + public void replaceAtSign() { + Assert.assertEquals("@", URLTools.replaceHexChars("%40")); + Assert.assertEquals("@@", URLTools.replaceHexChars("%40%40")); + Assert.assertEquals("abc@%xyz", URLTools.replaceHexChars("abc%40%xyz")); + } + + @Test + public void replaceAtAndPlusSign() { + Assert.assertEquals("@+", URLTools.replaceHexChars("%40%2B")); + Assert.assertEquals("@+@+", URLTools.replaceHexChars("%40%2B%40%2B")); + Assert.assertEquals("abc@+%xyz", URLTools.replaceHexChars("abc%40%2B%xyz")); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/UrlToolsTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/UrlToolsTest.xtend deleted file mode 100644 index 1ee8f4ac..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/UrlToolsTest.xtend +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test - -import org.junit.Assert -import org.junit.Test -import org.utplsql.sqldev.model.URLTools - -class UrlToolsTest { - val extension URLTools urlTools = new URLTools - - @Test - def void replacePlusSign() { - Assert.assertEquals("+", "%2B".replaceHexChars) - Assert.assertEquals("++", "%2B%2B".replaceHexChars) - Assert.assertEquals("abc+%xyz", "abc%2B%xyz".replaceHexChars) - } - - @Test - def void replaceAtSign() { - Assert.assertEquals("@", "%40".replaceHexChars) - Assert.assertEquals("@@", "%40%40".replaceHexChars) - Assert.assertEquals("abc@%xyz", "abc%40%xyz".replaceHexChars) - } - - @Test - def void replaceAtAndPlusSign() { - Assert.assertEquals("@+", "%40%2B".replaceHexChars) - Assert.assertEquals("@+@+", "%40%2B%40%2B".replaceHexChars) - Assert.assertEquals("abc@+%xyz", "abc%40%2B%xyz".replaceHexChars) - } - -} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterDialogTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterDialogTest.java new file mode 100644 index 00000000..5ada53d1 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterDialogTest.java @@ -0,0 +1,39 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.coverage; + +import java.util.Arrays; + +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.coverage.CodeCoverageReporter; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.SystemTools; +import org.utplsql.sqldev.test.AbstractJdbcTest; + + +public class CodeCoverageReporterDialogTest extends AbstractJdbcTest { + + @Test + public void layout() { + final CodeCoverageReporter reporter = new CodeCoverageReporter(Arrays.asList("SCOTT"), + Arrays.asList("a", "b", "c"), DatabaseTools.getConnection(dataSource)); + reporter.showParameterWindow(); + SystemTools.sleep(4 * 1000); + Assert.assertNotNull(reporter.getFrame()); + reporter.getFrame().exit(); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterDialogTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterDialogTest.xtend deleted file mode 100644 index ffd3e963..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterDialogTest.xtend +++ /dev/null @@ -1,32 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test.coverage - -import org.junit.Test -import org.utplsql.sqldev.coverage.CodeCoverageReporter -import org.utplsql.sqldev.test.AbstractJdbcTest - -class CodeCoverageReporterDialogTest extends AbstractJdbcTest{ - - @Test - def void layout() { - val reporter = new CodeCoverageReporter(#["SCOTT"], #['a', 'b', 'c'], dataSource.connection) - reporter.showParameterWindow - Thread.sleep(4 * 1000) - reporter.frame?.exit - } - -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterTest.java new file mode 100644 index 00000000..6aa90a94 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterTest.java @@ -0,0 +1,126 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.coverage; + +import java.io.File; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; +import java.sql.Connection; +import java.util.Arrays; +import java.util.Comparator; +import java.util.List; +import java.util.Optional; + +import org.junit.AfterClass; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.springframework.jdbc.datasource.SingleConnectionDataSource; +import org.utplsql.sqldev.coverage.CodeCoverageReporter; +import org.utplsql.sqldev.exception.GenericRuntimeException; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.FileTools; +import org.utplsql.sqldev.model.SystemTools; +import org.utplsql.sqldev.test.AbstractJdbcTest; + +public class CodeCoverageReporterTest extends AbstractJdbcTest { + + @BeforeClass + public static void setup() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE FUNCTION f RETURN INTEGER IS\n"); + sb.append("BEGIN\n"); + sb.append(" RETURN 1;\n"); + sb.append("END f;"); + jdbcTemplate.execute(sb.toString()); + sb.setLength(0); + sb.append("CREATE OR REPLACE PACKAGE test_f IS\n"); + sb.append(" --%suite\n\n"); + + sb.append(" --%test\n"); + sb.append(" PROCEDURE f;\n"); + sb.append("END test_f;"); + jdbcTemplate.execute(sb.toString()); + sb.setLength(0); + sb.append("CREATE OR REPLACE PACKAGE BODY test_f IS\n"); + sb.append(" --%test\n"); + sb.append(" PROCEDURE f IS\n"); + sb.append(" l_expected INTEGER := 1;\n"); + sb.append(" l_actual INTEGER;\n"); + sb.append(" BEGIN\n"); + sb.append(" l_actual := scott.f();\n"); + sb.append(" ut.expect(l_actual).to_equal(l_expected);\n"); + sb.append(" END f;\n"); + sb.append("END test_f;"); + jdbcTemplate.execute(sb.toString()); + } + + private File createTempFile(String prefix, String suffix) { + try { + return File.createTempFile("test", ".txt"); + } catch (IOException e) { + final String msg = "Cannot create temporary file with prefix '" + prefix + "' and suffix '" + suffix + "'."; + throw new GenericRuntimeException(msg, e); + } + } + + private Path getNewestOutputFile() { + final File file = createTempFile("test", ".txt"); + final File dir = file.getParentFile(); + file.delete(); + Optional last; + try { + last = Files.list(dir.toPath()) + .filter(f -> !f.toFile().isDirectory() && f.getFileName().toString().startsWith("utplsql_") + && f.getFileName().toString().endsWith(".html")) + .max(Comparator.comparingLong(f -> f.toFile().lastModified())); + } catch (IOException e) { + final String msg = "Cannot get newest output file in " + dir.getAbsolutePath() + "."; + throw new GenericRuntimeException(msg, e); + } + return last.orElse(null); + } + + @Test + public void produceReportAndCloseConnection() { + // create temporary dataSource, closed by reporter + SingleConnectionDataSource ds = new SingleConnectionDataSource(); + ds.setDriverClassName("oracle.jdbc.OracleDriver"); + ds.setUrl(dataSource.getUrl()); + ds.setUsername(dataSource.getUsername()); + ds.setPassword(dataSource.getPassword()); + final Connection conn = DatabaseTools.getConnection(ds); + final List pathList = Arrays.asList(":test_f"); + final List includeObjectList = Arrays.asList("f"); + final CodeCoverageReporter reporter = new CodeCoverageReporter(pathList, includeObjectList, conn); + final Thread run = reporter.runAsync(); + SystemTools.waitForThread(run, 20000); + Assert.assertTrue(DatabaseTools.isConnectionClosed(conn)); + final Path outputFile = this.getNewestOutputFile(); + Assert.assertNotNull(outputFile); + final String content = new String(FileTools.readFile(outputFile), StandardCharsets.UTF_8); + Assert.assertTrue( + content.contains("

SCOTT.F

100 % lines covered

")); + } + + @AfterClass + public static void teardown() { + executeAndIgnore(jdbcTemplate, "DROP PACKAGE test_f"); + executeAndIgnore(jdbcTemplate, "DROP FUNCTION f"); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterTest.xtend deleted file mode 100644 index 7f91d53b..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterTest.xtend +++ /dev/null @@ -1,113 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test.coverage - -import java.io.File -import java.nio.charset.StandardCharsets -import java.nio.file.Files -import java.nio.file.Path -import java.util.Comparator -import org.junit.AfterClass -import org.junit.Assert -import org.junit.BeforeClass -import org.junit.Test -import org.springframework.jdbc.BadSqlGrammarException -import org.springframework.jdbc.datasource.SingleConnectionDataSource -import org.utplsql.sqldev.coverage.CodeCoverageReporter -import org.utplsql.sqldev.test.AbstractJdbcTest - -class CodeCoverageReporterTest extends AbstractJdbcTest{ - - @BeforeClass - def static void setup() { - jdbcTemplate.execute(''' - CREATE OR REPLACE FUNCTION f RETURN INTEGER IS - BEGIN - RETURN 1; - END f; - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE test_f IS - --%suite - - --%test - PROCEDURE f; - END test_f; - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE BODY test_f IS - --%test - PROCEDURE f IS - l_expected INTEGER := 1; - l_actual INTEGER; - BEGIN - l_actual := scott.f(); - ut.expect(l_actual).to_equal(l_expected); - END f; - END test_f; - ''') - } - - private def Path getNewestOutputFile() { - val file = File.createTempFile("test", ".txt") - val dir = file.parentFile - file.delete - val last = Files.list(dir.toPath) - .filter([f | !f.toFile.directory]) - .filter([f | f.fileName.toString.startsWith("utplsql_")]) - .filter([f | f.fileName.toString.endsWith(".html")]) - .max(Comparator.comparingLong([f|f.toFile().lastModified()])) - return last.get - } - - @Test - def void produceReportAndCloseConnection() { - // create temporary dataSource, closed by reporter - var ds = new SingleConnectionDataSource() - ds.driverClassName = "oracle.jdbc.OracleDriver" - ds.url = dataSource.url - ds.username = dataSource.username - ds.password = dataSource.password - val conn = ds.connection - val pathList=#[':test_f'] - val includeObjectList = #['f'] - val reporter = new CodeCoverageReporter(pathList, includeObjectList, conn) - val run = reporter.runAsync - run.join(20000) - Assert.assertEquals(true, conn.isClosed) - val outputFile = getNewestOutputFile - Assert.assertTrue(outputFile !== null) - val content = new String(Files.readAllBytes(outputFile), StandardCharsets.UTF_8) - Assert.assertTrue(content.contains('

SCOTT.F

100 % lines covered

')) - } - - @AfterClass - def static void teardown() { - try { - jdbcTemplate.execute("DROP PACKAGE test_f") - } catch (BadSqlGrammarException e) { - // ignore - } - try { - jdbcTemplate.execute("DROP FUNCTION f") - } catch (BadSqlGrammarException e) { - // ignore - } - } - - - -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/DalBugFixTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/DalBugFixTest.java new file mode 100644 index 00000000..78fafe57 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/DalBugFixTest.java @@ -0,0 +1,99 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.dal; + +import java.util.List; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.oddgen.sqldev.generators.model.Node; +import org.utplsql.sqldev.dal.UtplsqlDao; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.test.AbstractJdbcTest; + +public class DalBugFixTest extends AbstractJdbcTest { + @Before + @After + public void setupAndTeardown() { + executeAndIgnore(jdbcTemplate, "DROP PACKAGE junit_utplsql_test_pkg"); + } + + @Test + // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/54 + public void issue54FolderIconForSuitesWithoutTests() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS\n"); + sb.append(" -- %suite\n\n"); + + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + final List actualNodes = dao.runnables(); + final Node pkg = actualNodes.stream().filter(it -> it.getId().equals("SCOTT:junit_utplsql_test_pkg")) + .findFirst().orElse(null); + Assert.assertNotNull(pkg); + Assert.assertEquals("FOLDER_ICON", pkg.getIconName()); + } + + @Test + // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/54 + public void issue54PackageIconForSuitesWithTests() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS\n"); + sb.append(" -- %suite\n\n"); + + sb.append(" -- %test\n"); + sb.append(" PROCEDURE t1;\n\n"); + + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + final List actualNodes = dao.runnables(); + final Node pkg = actualNodes.stream().filter(it -> it.getId().equals("SCOTT:junit_utplsql_test_pkg")) + .findFirst().orElse(null); + Assert.assertNotNull(pkg); + Assert.assertEquals("PACKAGE_ICON", pkg.getIconName()); + } + + @Test + // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/55 + public void issue55SuiteWithoutTests() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS\n"); + sb.append(" -- %suite\n\n"); + + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + final List actualNodes = dao.runnables(); + Assert.assertEquals(4, actualNodes.size()); + } + + @Test + public void issue56SuiteWithoutTests() { + // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/56 + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS\n"); + sb.append(" -- %suite\n\n"); + + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + Assert.assertTrue(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg")); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/DalBugFixTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/DalBugFixTest.xtend deleted file mode 100644 index d53fe158..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/DalBugFixTest.xtend +++ /dev/null @@ -1,107 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.utplsql.sqldev.test.dal - -import org.junit.AfterClass -import org.junit.Assert -import org.junit.BeforeClass -import org.junit.Test -import org.springframework.jdbc.BadSqlGrammarException -import org.utplsql.sqldev.dal.UtplsqlDao -import org.utplsql.sqldev.test.AbstractJdbcTest - -class DalBugFixTest extends AbstractJdbcTest { - - @BeforeClass - @AfterClass - def static void setupAndTeardown() { - try { - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test_pkg") - } catch (BadSqlGrammarException e) { - // ignore - } - } - - @Test - // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/54 - def void issue54FolderIconForSuitesWithoutTests() { - setupAndTeardown - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS - -- %suite - - END junit_utplsql_test_pkg; - ''') - val dao = new UtplsqlDao(dataSource.connection) - val actualNodes = dao.runnables() - Assert.assertEquals(4, actualNodes.size) - val pkg = actualNodes.findFirst[it.id == "SCOTT:junit_utplsql_test_pkg"] - Assert.assertEquals("FOLDER_ICON", pkg.iconName) - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test_pkg") - } - - @Test - // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/54 - def void issue54PackageIconForSuitesWithTests() { - setupAndTeardown - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS - -- %suite - - -- %test - PROCEDURE t1; - - END junit_utplsql_test_pkg; - ''') - val dao = new UtplsqlDao(dataSource.connection) - val actualNodes = dao.runnables() - Assert.assertEquals(6, actualNodes.size) - val pkg = actualNodes.findFirst[it.id == "SCOTT:junit_utplsql_test_pkg"] - Assert.assertEquals("PACKAGE_ICON", pkg.iconName) - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test_pkg") - } - - @Test - // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/55 - def void issue55SuiteWithoutTests() { - setupAndTeardown - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS - -- %suite - - END junit_utplsql_test_pkg; - ''') - val dao = new UtplsqlDao(dataSource.connection) - val actualNodes = dao.runnables() - Assert.assertEquals(4, actualNodes.size) - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test_pkg") - } - - @Test - // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/56 - def void issue56SuiteWithoutTests() { - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS - -- %suite - - END junit_utplsql_test_pkg; - ''') - val dao = new UtplsqlDao(dataSource.connection) - Assert.assertTrue(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg")) - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test_pkg") - } - -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/DalTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/DalTest.java new file mode 100644 index 00000000..d67019d6 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/DalTest.java @@ -0,0 +1,619 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.dal; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.oddgen.sqldev.generators.model.Node; +import org.utplsql.sqldev.dal.UtplsqlDao; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.ut.Annotation; +import org.utplsql.sqldev.test.AbstractJdbcTest; + +public class DalTest extends AbstractJdbcTest { + @Before + @After + public void setupAndTeardown() { + sysJdbcTemplate.execute("CREATE OR REPLACE PUBLIC SYNONYM ut FOR ut3.ut"); + executeAndIgnore(jdbcTemplate, "DROP PACKAGE junit_utplsql_test_pkg"); + executeAndIgnore(jdbcTemplate, "DROP PACKAGE BODY junit_utplsql_test_pkg"); + executeAndIgnore(jdbcTemplate, "DROP PACKAGE junit_no_test_pkg"); + executeAndIgnore(jdbcTemplate, "DROP TYPE junit_tab1_ot"); + executeAndIgnore(jdbcTemplate, "DROP TYPE junit_tab2_ot"); + executeAndIgnore(jdbcTemplate, "DROP FUNCTION junit_f"); + executeAndIgnore(jdbcTemplate, "DROP PROCEDURE junit_p"); + } + + @Test + public void isDbaViewAccessibleAsScott() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + Assert.assertFalse(dao.isDbaViewAccessible()); + } + + @Test + public void isDbaViewAccessibleAsSys() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(sysDataSource)); + Assert.assertTrue(dao.isDbaViewAccessible()); + } + + @Test + public void utplsqlSchemaWithoutPublicSynonym() { + sysJdbcTemplate.execute("DROP PUBLIC SYNONYM ut"); + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + Assert.assertEquals(null, dao.getUtplsqlSchema()); + } + + @Test + public void utplsqlSchemaWithPublicSynonym() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + Assert.assertEquals("UT3", dao.getUtplsqlSchema()); + } + + @Test + public void isUtAnnotationManagerInstalled() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + Assert.assertTrue(dao.isUtAnnotationManagerInstalled()); + } + + private void containsUtplsqlTestWithSuiteAnnotation(final String utPlsqlVersion) { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.setUtPlsqlVersion(utPlsqlVersion); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS\n"); + sb.append(" -- %suite\n\n"); + + sb.append(" -- %test\n"); + sb.append(" PROCEDURE t1;\n\n"); + + sb.append(" -- %Test\n"); + sb.append(" PROCEDURE t2;\n\n"); + + sb.append(" PROCEDURE t3;\n"); + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + Assert.assertTrue(dao.containsUtplsqlTest("scott")); + Assert.assertTrue(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg")); + Assert.assertTrue(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg", "t1")); + Assert.assertTrue(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg", "t2")); + Assert.assertFalse(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg", "t3")); + } + + @Test + public void containsUtplsqlTestWithSuiteAnnotation304() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + if (dao.normalizedUtPlsqlVersionNumber() < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { + containsUtplsqlTestWithSuiteAnnotation("3.0.4"); + } + } + + @Test + public void containsUtplsqlTestWithSuiteAnnotation313() { + containsUtplsqlTestWithSuiteAnnotation("3.1.3"); + } + + @Test + public void containsUtplsqlTestWithSuiteAnnotation318() { + containsUtplsqlTestWithSuiteAnnotation("3.1.8"); + } + + private void containsUtplsqlTestWithoutSuiteAnnotation(final String utPlsqlVersion) { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.setUtPlsqlVersion(utPlsqlVersion); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS\n"); + sb.append(" -- %test\n"); + sb.append(" PROCEDURE t1;\n\n"); + + sb.append(" -- %Test\n"); + sb.append(" PROCEDURE t2;\n\n"); + + sb.append(" PROCEDURE t3;\n"); + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + Assert.assertFalse(dao.containsUtplsqlTest("scott")); + Assert.assertFalse(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg")); + Assert.assertFalse(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg", "t1")); + Assert.assertFalse(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg", "t2")); + Assert.assertFalse(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg", "t3")); + } + + @Test + public void containsUtplsqlTestWithoutSuiteAnnotation304() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + if (dao.normalizedUtPlsqlVersionNumber() < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { + containsUtplsqlTestWithoutSuiteAnnotation("3.0.4"); + } + } + + @Test + public void containsUtplsqlTestWithoutSuiteAnnotation313() { + containsUtplsqlTestWithoutSuiteAnnotation("3.1.3"); + } + + @Test + public void containsUtplsqlTestWithoutSuiteAnnotation318() { + containsUtplsqlTestWithoutSuiteAnnotation("3.1.8"); + } + + private void annotations(final String utPlsqlVersion) { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.setUtPlsqlVersion(utPlsqlVersion); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS\n"); + sb.append(" -- %suite\n\n"); + + sb.append(" -- %test\n"); + sb.append(" PROCEDURE t1;\n\n"); + + sb.append(" -- %Test\n"); + sb.append(" PROCEDURE t2;\n\n"); + + sb.append(" PROCEDURE t3;\n"); + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + final List actual = dao.annotations("scott", "junit_utplsql_test_pkg"); + final ArrayList expected = new ArrayList(); + final Annotation suite = new Annotation(); + suite.setObjectOwner("SCOTT"); + suite.setObjectName("JUNIT_UTPLSQL_TEST_PKG"); + suite.setName("suite"); + suite.setSubobjectName(suite.getObjectName()); + expected.add(suite); + final Annotation t1 = new Annotation(); + t1.setObjectOwner("SCOTT"); + t1.setObjectName("JUNIT_UTPLSQL_TEST_PKG"); + t1.setName("test"); + t1.setSubobjectName("T1"); + expected.add(t1); + final Annotation t2 = new Annotation(); + t2.setObjectOwner("SCOTT"); + t2.setObjectName("JUNIT_UTPLSQL_TEST_PKG"); + t2.setName("test"); + t2.setSubobjectName("T2"); + expected.add(t2); + Assert.assertEquals(expected.toString(), actual.toString()); + } + + @Test + public void annotations304() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + if (dao.normalizedUtPlsqlVersionNumber() < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { + annotations("3.0.4"); + } + } + + @Test + public void annotations313() { + annotations("3.1.3"); + } + + @Test + public void annotations318() { + annotations("3.1.8"); + } + + private void testablesPackagesWithTests(final String utPlsqlVersion) { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.setUtPlsqlVersion(utPlsqlVersion); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS\n"); + sb.append(" -- %suite\n\n"); + + sb.append(" -- %test\n"); + sb.append(" PROCEDURE t1;\n\n"); + + sb.append(" -- %Test\n"); + sb.append(" PROCEDURE t2;\n\n"); + + sb.append(" PROCEDURE t3;\n\n"); + + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + final List actual = dao.testables("PACKAGE"); + Assert.assertEquals(0, actual.size()); + } + + @Test + public void testablesPackagesWithTests304() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + if (dao.normalizedUtPlsqlVersionNumber() < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { + testablesPackagesWithTests("3.0.4"); + } + } + + @Test + public void testablesPackagesWithTests313() { + testablesPackagesWithTests("3.1.3"); + } + + @Test + public void testablesPackagesWithTests318() { + testablesPackagesWithTests("3.1.8"); + } + + private void testablesPackagesWithoutTests(final String utPlsqlVersion) { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.setUtPlsqlVersion(utPlsqlVersion); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_no_test_pkg IS\n"); + sb.append(" PROCEDURE p1;\n\n"); + + sb.append(" PROCEDURE p2;\n"); + sb.append("END junit_no_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + final List actual = dao.testables("PACKAGE"); + Assert.assertEquals(1, actual.size()); + Assert.assertEquals("PACKAGE.JUNIT_NO_TEST_PKG", actual.get(0).getId()); + } + + @Test + public void testablesPackagesWithoutTests304() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + if (dao.normalizedUtPlsqlVersionNumber() < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { + testablesPackagesWithoutTests("3.0.4"); + } + } + + @Test + public void testablesPackagesWithoutTests313() { + testablesPackagesWithoutTests("3.1.3"); + } + + @Test + public void testablesPackagesWithoutTests318() { + testablesPackagesWithoutTests("3.1.8"); + } + + private void testablesTypes(final String utPlsqlVersion) { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.setUtPlsqlVersion(utPlsqlVersion); + jdbcTemplate.execute("CREATE OR REPLACE TYPE junit_tab1_ot IS object (a integer, b integer);"); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE TYPE junit_tab2_ot IS object (\n"); + sb.append(" a integer,\n"); + sb.append(" b integer,\n"); + sb.append(" member procedure c(\n"); + sb.append(" self in out nocopy junit_tab2_ot,\n"); + sb.append(" p integer\n"); + sb.append(" )\n"); + sb.append(");"); + jdbcTemplate.execute(sb.toString()); + final List actual = dao.testables("TYPE"); + Assert.assertEquals(1, actual.size()); + Assert.assertEquals("TYPE.JUNIT_TAB2_OT", actual.get(0).getId()); + } + + @Test + public void testablesTypes304() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + if (dao.normalizedUtPlsqlVersionNumber() < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { + testablesTypes("3.0.4"); + } + } + + @Test + public void testablesTypes313() { + testablesTypes("3.1.3"); + } + + @Test + public void testablesTypes318() { + testablesTypes("3.1.8"); + } + + private void testablesFunctions(final String utPlsqlVersion) { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.setUtPlsqlVersion(utPlsqlVersion); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE FUNCTION junit_f RETURN INTEGER IS\n"); + sb.append("BEGIN\n"); + sb.append(" RETURN 1;\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + final List actual = dao.testables("FUNCTION"); + Assert.assertEquals(1, actual.size()); + Assert.assertEquals("FUNCTION.JUNIT_F", actual.get(0).getId()); + } + + @Test + public void testablesFunctions304() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + if (dao.normalizedUtPlsqlVersionNumber() < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { + testablesFunctions("3.0.4"); + } + } + + @Test + public void testablesFunctions313() { + testablesFunctions("3.1.3"); + } + + @Test + public void testablesFunctions318() { + testablesFunctions("3.1.8"); + } + + public void testablesProcedures(final String utPlsqlVersion) { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.setUtPlsqlVersion(utPlsqlVersion); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PROCEDURE junit_p RETURN INTEGER IS\n"); + sb.append("BEGIN\n"); + sb.append(" NULL;\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + final List actual = dao.testables("PROCEDURE"); + Assert.assertEquals(1, actual.size()); + Assert.assertEquals("PROCEDURE.JUNIT_P", actual.get(0).getId()); + } + + @Test + public void testablesProcedures304() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + if (dao.normalizedUtPlsqlVersionNumber() < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { + testablesProcedures("3.0.4"); + } + } + + @Test + public void testablesProcedures313() { + testablesProcedures("3.1.3"); + } + + @Test + public void testablesProcedures318() { + testablesProcedures("3.1.8"); + } + + public void runnables(final String utPlsqlVersion) { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS\n"); + sb.append(" -- %suite\n"); + sb.append(" -- %suitepath(a.B.c)\n\n"); + + sb.append(" -- %test\n"); + sb.append(" PROCEDURE T0;\n\n"); + + sb.append(" -- %context(myContext)\n\n"); + + sb.append(" -- %test(t1: test One)\n"); + sb.append(" PROCEDURE t1;\n\n"); + + sb.append(" -- %test(t2: test Two)\n"); + sb.append(" PROCEDURE t2;\n\n"); + + sb.append(" -- %endcontext\n\n"); + + sb.append(" -- %test\n"); + sb.append(" PROCEDURE t3;\n"); + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + final List actualNodes = dao.runnables(); + Assert.assertEquals(16, actualNodes.size()); + final HashMap actual = new HashMap(); + for (final Node node : actualNodes) { + actual.put(node.getId(), node.getParentId()); + } + Assert.assertEquals(null, actual.get("SUITE")); + Assert.assertEquals("SUITE", actual.get("SCOTT.JUNIT_UTPLSQL_TEST_PKG")); + Assert.assertEquals("SCOTT.JUNIT_UTPLSQL_TEST_PKG", actual.get("SCOTT.JUNIT_UTPLSQL_TEST_PKG.T0")); + Assert.assertEquals("SCOTT.JUNIT_UTPLSQL_TEST_PKG", actual.get("SCOTT.JUNIT_UTPLSQL_TEST_PKG.T1")); + Assert.assertEquals("SCOTT.JUNIT_UTPLSQL_TEST_PKG", actual.get("SCOTT.JUNIT_UTPLSQL_TEST_PKG.T2")); + Assert.assertEquals("SCOTT.JUNIT_UTPLSQL_TEST_PKG", actual.get("SCOTT.JUNIT_UTPLSQL_TEST_PKG.T3")); + Assert.assertEquals(null, actual.get("SUITEPATH")); + Assert.assertEquals("SUITEPATH", actual.get("SCOTT:a")); + Assert.assertEquals("SCOTT:a", actual.get("SCOTT:a.b")); + Assert.assertEquals("SCOTT:a.b", actual.get("SCOTT:a.b.c")); + Assert.assertEquals("SCOTT:a.b.c", actual.get("SCOTT:a.b.c.junit_utplsql_test_pkg")); + Assert.assertEquals("SCOTT:a.b.c.junit_utplsql_test_pkg.nested_context_#", + actual.get("SCOTT:a.b.c.junit_utplsql_test_pkg.nested_context_#1")); + Assert.assertEquals("SCOTT:a.b.c.junit_utplsql_test_pkg", + actual.get("SCOTT:a.b.c.junit_utplsql_test_pkg.t0")); + Assert.assertEquals("SCOTT:a.b.c.junit_utplsql_test_pkg", + actual.get("SCOTT:a.b.c.junit_utplsql_test_pkg.t3")); + Assert.assertEquals("SCOTT:a.b.c.junit_utplsql_test_pkg.nested_context_#1", + actual.get("SCOTT:a.b.c.junit_utplsql_test_pkg.nested_context_#1.t1")); + Assert.assertEquals("SCOTT:a.b.c.junit_utplsql_test_pkg.nested_context_#1", + actual.get("SCOTT:a.b.c.junit_utplsql_test_pkg.nested_context_#1.t2")); + } + + @Test + public void runnables304() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + if (dao.normalizedUtPlsqlVersionNumber() < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { + runnables("3.0.4"); + } + } + + @Test + public void runnables313() { + runnables("3.1.3"); + } + + @Test + public void runnables318() { + runnables("3.1.8"); + } + + @Test + public void dbmsOutput() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.enableDbmsOutput(); + StringBuilder sb = new StringBuilder(); + sb.append("BEGIN\n"); + sb.append(" sys.dbms_output.put_line('line1');\n"); + sb.append(" sys.dbms_output.put_line('line2');\n"); + sb.append(" sys.dbms_output.put_line(null);\n"); + sb.append(" sys.dbms_output.put_line('line4');\n"); + sb.append(" sys.dbms_output.put_line('line5');\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + final String actual = dao.getDbmsOutput(2); + final String expected = "line1\nline2\n\nline4\nline5\n"; + Assert.assertEquals(expected, actual); + } + + @Test + public void htmlCodeCoverage() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + final String actual = dao.htmlCodeCoverage(Arrays.asList("SCOTT"), Arrays.asList("scott"), Arrays.asList(), + Arrays.asList()); + Assert.assertTrue(actual.startsWith("")); + Assert.assertTrue(actual.trim().endsWith("")); + } + + @Test + public void includes() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE FUNCTION junit_f RETURN INTEGER IS\n"); + sb.append("BEGIN\n"); + sb.append(" RETURN 1;\n"); + sb.append("END junit_f;"); + jdbcTemplate.execute(sb.toString()); + sb.setLength(0); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS\n"); + sb.append(" -- %suite\n\n"); + + sb.append(" -- %test\n"); + sb.append(" PROCEDURE f1;\n"); + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + sb.setLength(0); + sb.append("CREATE OR REPLACE PACKAGE BODY junit_utplsql_test_pkg IS\n"); + sb.append(" PROCEDURE f1 IS\n"); + sb.append(" l_expected INTEGER := 1;\n"); + sb.append(" l_actual INTEGER;\n"); + sb.append(" BEGIN\n"); + sb.append(" l_actual := junit_f;\n"); + sb.append(" ut.expect(l_actual).to_equal(l_expected);\n"); + sb.append(" END f1;\n"); + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + final List actualEmpty = dao.includes("SCOTT", "TEST_F1"); + Assert.assertEquals(Arrays.asList(), actualEmpty); + final List actual = dao.includes("SCOTT", "junit_utplsql_test_pkg"); + Assert.assertTrue(actual.stream().anyMatch(it -> it.equals("SCOTT.JUNIT_UTPLSQL_TEST_PKG"))); + Assert.assertTrue(actual.stream().anyMatch(it -> it.equals("SCOTT.JUNIT_F"))); + Assert.assertTrue(actual.stream().anyMatch(it -> it.equals("UT3.UT_EXPECTATION"))); + } + + @Test + public void normalizedPlsqlVersionOkRelease() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.setUtPlsqlVersion("v3.1.10.1234"); + final String actual = dao.normalizedUtPlsqlVersion(); + Assert.assertEquals("3.1.10", actual); + } + + @Test + public void normalizedPlsqlVersionOkDevelop() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.setUtPlsqlVersion("v3.1.10.1234-develop"); + final String actual = dao.normalizedUtPlsqlVersion(); + Assert.assertEquals("3.1.10", actual); + } + + @Test + public void normalizedPlsqlVersionNok() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.setUtPlsqlVersion("bla bla 1.2"); + final String actual = dao.normalizedUtPlsqlVersion(); + Assert.assertEquals("0.0.0", actual); + } + + @Test + public void normaliedPlsqlVersionNumber() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + dao.setUtPlsqlVersion("3.14.37"); + final int actual = dao.normalizedUtPlsqlVersionNumber(); + Assert.assertEquals(3014037, actual); + } + + @Test + public void utPlsqlVersion() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + final String actual = dao.getUtPlsqlVersion(); + final String sql = "SELECT ut.version FROM DUAL"; + final String expected = jdbcTemplate.queryForObject(sql, String.class); + Assert.assertEquals(expected, actual); + } + + @Test + public void getSourceOfPackage() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS\n"); + sb.append(" -- %suite\n\n"); + + sb.append(" -- %test\n"); + sb.append(" PROCEDURE p1;\n"); + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + final String actual = dao.getSource("SCOTT", "PACKAGE", "JUNIT_UTPLSQL_TEST_PKG"); + Assert.assertTrue(actual.contains("-- %suite")); + Assert.assertTrue(actual.contains("PROCEDURE p1;")); + } + + @Test + public void getSourceOfPackageBody() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE BODY junit_utplsql_test_pkg IS\n"); + sb.append(" PROCEDURE p1 IS\n"); + sb.append(" l_expected INTEGER := 1;\n"); + sb.append(" l_actual INTEGER;\n"); + sb.append(" BEGIN\n"); + sb.append(" l_actual := junit_f;\n"); + sb.append(" ut.expect(l_actual).to_equal(l_expected);\n"); + sb.append(" END p1;\n"); + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + final String actual = dao.getSource("SCOTT", "PACKAGE BODY", "JUNIT_UTPLSQL_TEST_PKG"); + Assert.assertTrue(actual.contains("PACKAGE BODY")); + Assert.assertTrue(actual.contains("PROCEDURE p1 IS")); + } + + @Test + public void getObjectType() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS\n"); + sb.append(" -- %suite\n\n"); + + sb.append(" -- %test\n"); + sb.append(" PROCEDURE p1;\n"); + sb.append("END junit_utplsql_test_pkg;"); + jdbcTemplate.execute(sb.toString()); + final String actual = dao.getObjectType("SCOTT", "JUNIT_UTPLSQL_TEST_PKG"); + Assert.assertEquals("PACKAGE", actual); + } + + @Test + public void normalizedUtPlsqlVersion() { + final UtplsqlDao dao = new UtplsqlDao(DatabaseTools.getConnection(dataSource)); + final String version = dao.normalizedUtPlsqlVersion(); + Assert.assertTrue(version != null); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/DalTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/DalTest.xtend deleted file mode 100644 index 2a8d5444..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/DalTest.xtend +++ /dev/null @@ -1,599 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.utplsql.sqldev.test.dal - -import java.util.ArrayList -import java.util.HashMap -import org.junit.AfterClass -import org.junit.Assert -import org.junit.BeforeClass -import org.junit.Test -import org.springframework.jdbc.BadSqlGrammarException -import org.utplsql.sqldev.dal.UtplsqlDao -import org.utplsql.sqldev.model.ut.Annotation -import org.utplsql.sqldev.test.AbstractJdbcTest - -class DalTest extends AbstractJdbcTest { - - @BeforeClass - @AfterClass - def static void setupAndTeardown() { - sysJdbcTemplate.execute("CREATE OR REPLACE PUBLIC SYNONYM ut FOR ut3.ut") - try { - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test_pkg") - } catch (BadSqlGrammarException e) { - // ignore - } - try { - jdbcTemplate.execute("DROP PACKAGE BODY junit_utplsql_test_pkg") - } catch (BadSqlGrammarException e) { - // ignore - } - try { - jdbcTemplate.execute("DROP PACKAGE junit_no_test_pkg") - } catch (BadSqlGrammarException e) { - // ignore - } - try { - jdbcTemplate.execute("DROP TYPE junit_tab1_ot") - } catch (BadSqlGrammarException e) { - // ignore - } - try { - jdbcTemplate.execute("DROP TYPE junit_tab2_ot") - } catch (BadSqlGrammarException e) { - // ignore - } - try { - jdbcTemplate.execute("DROP FUNCTION junit_f") - } catch (BadSqlGrammarException e) { - // ignore - } - try { - jdbcTemplate.execute("DROP PROCEDURE junit_p") - } catch (BadSqlGrammarException e) { - // ignore - } - } - - @Test - def void isDbaViewAccessible() { - val dao = new UtplsqlDao(dataSource.connection) - Assert.assertFalse(dao.dbaViewAccessible) - val sysDao = new UtplsqlDao(sysDataSource.connection) - Assert.assertTrue(sysDao.dbaViewAccessible) - } - - @Test - def void utplsqlSchema() { - sysJdbcTemplate.execute("DROP PUBLIC SYNONYM ut") - val dao = new UtplsqlDao(dataSource.connection) - Assert.assertEquals(null, dao.utplsqlSchema) - setupAndTeardown - Assert.assertEquals("UT3", dao.utplsqlSchema) - } - - @Test - def void isUtAnnotationManagerInstalled() { - val dao = new UtplsqlDao(dataSource.connection) - Assert.assertTrue(dao.utAnnotationManagerInstalled) - } - - def void containsUtplsqlTest(String utPlsqlVersion) { - val dao = new UtplsqlDao(dataSource.connection) - dao.utPlsqlVersion = utPlsqlVersion - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS - -- %suite - - -- %test - PROCEDURE t1; - - -- %Test - PROCEDURE t2; - - PROCEDURE t3; - END junit_utplsql_test_pkg; - ''') - Assert.assertTrue(dao.containsUtplsqlTest("scott")) - Assert.assertTrue(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg")) - Assert.assertTrue(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg", "t1")) - Assert.assertTrue(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg", "t2")) - Assert.assertFalse(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg", "t3")) - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS - -- %test - PROCEDURE t1; - - -- %Test - PROCEDURE t2; - - PROCEDURE t3; - END junit_utplsql_test_pkg; - ''') - Assert.assertFalse(dao.containsUtplsqlTest("scott")) - Assert.assertFalse(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg")) - Assert.assertFalse(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg", "t1")) - Assert.assertFalse(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg", "t2")) - Assert.assertFalse(dao.containsUtplsqlTest("scott", "junit_utplsql_test_pkg", "t3")) - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test_pkg") - } - - @Test - def void containsUtplsqlTest304() { - val dao = new UtplsqlDao(dataSource.connection) - if (dao.normalizedUtPlsqlVersionNumber < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { - containsUtplsqlTest("3.0.4") - } - } - - @Test - def void containsUtplsqlTest313() { - containsUtplsqlTest("3.1.3") - } - - @Test - def void containsUtplsqlTest318() { - containsUtplsqlTest("3.1.8") - } - - def void annotations(String utPlsqlVersion) { - val dao = new UtplsqlDao(dataSource.connection) - dao.utPlsqlVersion = utPlsqlVersion - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS - -- %suite - - -- %test - PROCEDURE t1; - - -- %Test - PROCEDURE t2; - - PROCEDURE t3; - END junit_utplsql_test_pkg; - ''') - val actual = dao.annotations("scott", "junit_utplsql_test_pkg") - val expected = new ArrayList - val suite = new Annotation - suite.objectOwner = "SCOTT" - suite.objectName = "JUNIT_UTPLSQL_TEST_PKG" - suite.name = 'suite' - suite.subobjectName = suite.objectName - expected.add(suite) - val t1 = new Annotation - t1.objectOwner = "SCOTT" - t1.objectName = "JUNIT_UTPLSQL_TEST_PKG" - t1.name = 'test' - t1.subobjectName = 'T1' - expected.add(t1) - val t2 = new Annotation - t2.objectOwner = "SCOTT" - t2.objectName = "JUNIT_UTPLSQL_TEST_PKG" - t2.name = 'test' - t2.subobjectName = 'T2' - expected.add(t2) - Assert.assertEquals(expected.toString, actual.toString) - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test_pkg") - } - - @Test - def void annotations304() { - val dao = new UtplsqlDao(dataSource.connection) - if (dao.normalizedUtPlsqlVersionNumber < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { - annotations("3.0.4") - } - } - - @Test - def void annotations313() { - annotations("3.1.3") - } - - @Test - def void annotations318() { - annotations("3.1.8") - } - - def void testablesPackages(String utPlsqlVersion) { - val dao = new UtplsqlDao(dataSource.connection) - dao.utPlsqlVersion = utPlsqlVersion - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS - -- %suite - - -- %test - PROCEDURE t1; - - -- %Test - PROCEDURE t2; - - PROCEDURE t3; - END junit_utplsql_test_pkg; - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_no_test_pkg IS - PROCEDURE p1; - - PROCEDURE p2; - END junit_no_test_pkg; - ''') - val actual = dao.testables('PACKAGE') - Assert.assertEquals(1, actual.size) - Assert.assertEquals("PACKAGE.JUNIT_NO_TEST_PKG", actual.get(0).id) - } - - @Test - def void testablesPackages304() { - val dao = new UtplsqlDao(dataSource.connection) - if (dao.normalizedUtPlsqlVersionNumber < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { - testablesPackages("3.0.4") - } - } - - @Test - def void testablesPackages313() { - testablesPackages("3.1.3") - } - - @Test - def void testablesPackages318() { - testablesPackages("3.1.8") - } - - def void testablesTypes(String utPlsqlVersion) { - val dao = new UtplsqlDao(dataSource.connection) - dao.utPlsqlVersion = utPlsqlVersion - jdbcTemplate.execute(''' - CREATE OR REPLACE TYPE junit_tab1_ot IS object (a integer, b integer); - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE TYPE junit_tab2_ot IS object ( - a integer, - b integer, - member procedure c( - self in out nocopy junit_tab2_ot, - p integer - ) - ); - ''') - val actual = dao.testables('TYPE') - Assert.assertEquals(1, actual.size) - Assert.assertEquals("TYPE.JUNIT_TAB2_OT", actual.get(0).id) - } - - @Test - def void testablesTypes304() { - val dao = new UtplsqlDao(dataSource.connection) - if (dao.normalizedUtPlsqlVersionNumber < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { - testablesTypes("3.0.4") - } - } - - @Test - def void testablesTypes313() { - testablesTypes("3.1.3") - } - - @Test - def void testablesTypes318() { - testablesTypes("3.1.8") - } - - def void testablesFunctions(String utPlsqlVersion) { - val dao = new UtplsqlDao(dataSource.connection) - dao.utPlsqlVersion = utPlsqlVersion - jdbcTemplate.execute(''' - CREATE OR REPLACE FUNCTION junit_f RETURN INTEGER IS - BEGIN - RETURN 1; - END; - ''') - val actual = dao.testables('FUNCTION') - Assert.assertEquals(1, actual.size) - Assert.assertEquals("FUNCTION.JUNIT_F", actual.get(0).id) - } - - @Test - def void testablesFunctions304() { - val dao = new UtplsqlDao(dataSource.connection) - if (dao.normalizedUtPlsqlVersionNumber < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { - testablesFunctions("3.0.4") - } - } - - @Test - def void testablesFunctions313() { - testablesFunctions("3.1.3") - } - - @Test - def void testablesFunctions318() { - testablesFunctions("3.1.8") - } - - def void testablesProcedures(String utPlsqlVersion) { - val dao = new UtplsqlDao(dataSource.connection) - dao.utPlsqlVersion = utPlsqlVersion - jdbcTemplate.execute(''' - CREATE OR REPLACE PROCEDURE junit_p RETURN INTEGER IS - BEGIN - NULL; - END; - ''') - val actual = dao.testables('PROCEDURE') - Assert.assertEquals(1, actual.size) - Assert.assertEquals("PROCEDURE.JUNIT_P", actual.get(0).id) - } - - @Test - def void testablesProcedures304() { - val dao = new UtplsqlDao(dataSource.connection) - if (dao.normalizedUtPlsqlVersionNumber < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { - testablesProcedures("3.0.4") - } - } - - @Test - def void testablesProcedures313() { - testablesProcedures("3.1.3") - } - - @Test - def void testablesProcedures318() { - testablesProcedures("3.1.8") - } - - def void runnables(String utPlsqlVersion) { - val dao = new UtplsqlDao(dataSource.connection) - dao.utPlsqlVersion = utPlsqlVersion - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS - -- %suite - -- %suitepath(a.B.c) - - -- %test - PROCEDURE T0; - - -- %context(myContext) - - -- %test(t1: test One) - PROCEDURE t1; - - -- %test(t2: test Two) - PROCEDURE t2; - - -- %endcontext - - -- %test - PROCEDURE t3; - END junit_utplsql_test_pkg; - ''') - val actualNodes = dao.runnables() - Assert.assertEquals(16, actualNodes.size) - val actual = new HashMap - for (node : actualNodes) { - actual.put(node.id, node.parentId) - } - Assert.assertEquals(null, actual.get("SUITE")) - Assert.assertEquals("SUITE", actual.get("SCOTT.JUNIT_UTPLSQL_TEST_PKG")) - Assert.assertEquals("SCOTT.JUNIT_UTPLSQL_TEST_PKG", actual.get("SCOTT.JUNIT_UTPLSQL_TEST_PKG.T0")) - Assert.assertEquals("SCOTT.JUNIT_UTPLSQL_TEST_PKG", actual.get("SCOTT.JUNIT_UTPLSQL_TEST_PKG.T1")) - Assert.assertEquals("SCOTT.JUNIT_UTPLSQL_TEST_PKG", actual.get("SCOTT.JUNIT_UTPLSQL_TEST_PKG.T2")) - Assert.assertEquals("SCOTT.JUNIT_UTPLSQL_TEST_PKG", actual.get("SCOTT.JUNIT_UTPLSQL_TEST_PKG.T3")) - Assert.assertEquals(null, actual.get("SUITEPATH")) - Assert.assertEquals("SUITEPATH", actual.get("SCOTT:a")) - Assert.assertEquals("SCOTT:a", actual.get("SCOTT:a.b")) - Assert.assertEquals("SCOTT:a.b", actual.get("SCOTT:a.b.c")) - Assert.assertEquals("SCOTT:a.b.c", actual.get("SCOTT:a.b.c.junit_utplsql_test_pkg")) - Assert.assertEquals("SCOTT:a.b.c.junit_utplsql_test_pkg.nested_context_#", actual.get("SCOTT:a.b.c.junit_utplsql_test_pkg.nested_context_#1")) - Assert.assertEquals("SCOTT:a.b.c.junit_utplsql_test_pkg", actual.get("SCOTT:a.b.c.junit_utplsql_test_pkg.t0")) - Assert.assertEquals("SCOTT:a.b.c.junit_utplsql_test_pkg", actual.get("SCOTT:a.b.c.junit_utplsql_test_pkg.t3")) - Assert.assertEquals("SCOTT:a.b.c.junit_utplsql_test_pkg.nested_context_#1", actual.get("SCOTT:a.b.c.junit_utplsql_test_pkg.nested_context_#1.t1")) - Assert.assertEquals("SCOTT:a.b.c.junit_utplsql_test_pkg.nested_context_#1", actual.get("SCOTT:a.b.c.junit_utplsql_test_pkg.nested_context_#1.t2")) - } - - @Test - def void runnables304() { - val dao = new UtplsqlDao(dataSource.connection) - if (dao.normalizedUtPlsqlVersionNumber < UtplsqlDao.FIRST_VERSION_WITHOUT_INTERNAL_API) { - runnables("3.0.4") - } - } - - @Test - def void runnables313() { - runnables("3.1.3") - } - - @Test - def void runnables318() { - runnables("3.1.8") - } - - @Test - def void dbmsOutput() { - val dao = new UtplsqlDao(dataSource.connection) - dao.enableDbmsOutput - jdbcTemplate.execute(''' - BEGIN - sys.dbms_output.put_line('line1'); - sys.dbms_output.put_line('line2'); - sys.dbms_output.put_line(null); - sys.dbms_output.put_line('line4'); - sys.dbms_output.put_line('line5'); - END; - ''') - val actual = dao.getDbmsOutput(2) - val expected = ''' - line1 - line2 - - line4 - line5 - ''' - Assert.assertEquals(expected, actual) - } - - @Test - def void htmlCodeCoverage() { - setupAndTeardown - val dao = new UtplsqlDao(dataSource.connection) - val actual = dao.htmlCodeCoverage(#["SCOTT"], #['scott'], #[], #[]) - Assert.assertTrue(actual.startsWith("")) - Assert.assertTrue(actual.trim.endsWith("")) - } - - @Test - def void includes() { - setupAndTeardown - jdbcTemplate.execute(''' - CREATE OR REPLACE FUNCTION junit_f RETURN INTEGER IS - BEGIN - RETURN 1; - END junit_f; - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS - -- %suite - - -- %test - PROCEDURE f1; - END junit_utplsql_test_pkg; - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE BODY junit_utplsql_test_pkg IS - PROCEDURE f1 IS - l_expected INTEGER := 1; - l_actual INTEGER; - BEGIN - l_actual := junit_f; - ut.expect(l_actual).to_equal(l_expected); - END f1; - END junit_utplsql_test_pkg; - ''') - val dao = new UtplsqlDao(dataSource.connection) - val actualEmpty = dao.includes('SCOTT', 'TEST_F1') - Assert.assertEquals(#[], actualEmpty) - val actual = dao.includes('SCOTT', 'junit_utplsql_test_pkg') - Assert.assertTrue(actual.findFirst[it == "SCOTT.JUNIT_UTPLSQL_TEST_PKG"] !== null) - Assert.assertTrue(actual.findFirst[it == "SCOTT.JUNIT_F"] !== null) - Assert.assertTrue(actual.findFirst[it == "UT3.UT_EXPECTATION"] !== null) - } - - @Test - def void normalizedPlsqlVersionOkRelease() { - val dao = new UtplsqlDao(dataSource.connection) - dao.utPlsqlVersion = "v3.1.10.1234" - val actual = dao.normalizedUtPlsqlVersion() - Assert.assertEquals("3.1.10", actual) - } - - @Test - def void normalizedPlsqlVersionOkDevelop() { - val dao = new UtplsqlDao(dataSource.connection) - dao.utPlsqlVersion = "v3.1.10.1234-develop" - val actual = dao.normalizedUtPlsqlVersion() - Assert.assertEquals("3.1.10", actual) - } - - @Test - def void normalizedPlsqlVersionNok() { - val dao = new UtplsqlDao(dataSource.connection) - dao.utPlsqlVersion = "bla bla 1.2" - val actual = dao.normalizedUtPlsqlVersion() - Assert.assertEquals("0.0.0", actual) - } - - @Test - def void normaliedPlsqlVersionNumber() { - val dao = new UtplsqlDao(dataSource.connection) - dao.utPlsqlVersion = "3.14.37" - val actual = dao.normalizedUtPlsqlVersionNumber() - Assert.assertEquals(3014037, actual) - } - - @Test - def void utPlsqlVersion() { - val dao = new UtplsqlDao(dataSource.connection) - val actual = dao.utPlsqlVersion - val sql = "SELECT ut.version FROM DUAL" - val expected = jdbcTemplate.queryForObject(sql, String) - Assert.assertEquals(expected, actual) - - } - - @Test - def void getSourceOfPackage() { - val dao = new UtplsqlDao(dataSource.connection) - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS - -- %suite - - -- %test - PROCEDURE p1; - END junit_utplsql_test_pkg; - ''') - val actual = dao.getSource("SCOTT", "PACKAGE", "JUNIT_UTPLSQL_TEST_PKG") - Assert.assertTrue(actual.contains("-- %suite")) - Assert.assertTrue(actual.contains("PROCEDURE p1;")) - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test_pkg") - } - - @Test - def void getSourceOfPackageBody() { - val dao = new UtplsqlDao(dataSource.connection) - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE BODY junit_utplsql_test_pkg IS - PROCEDURE p1 IS - l_expected INTEGER := 1; - l_actual INTEGER; - BEGIN - l_actual := junit_f; - ut.expect(l_actual).to_equal(l_expected); - END p1; - END junit_utplsql_test_pkg; - '''); - val actual = dao.getSource("SCOTT", "PACKAGE BODY", "JUNIT_UTPLSQL_TEST_PKG") - Assert.assertTrue(actual.contains("PACKAGE BODY")) - Assert.assertTrue(actual.contains("PROCEDURE p1 IS")) - jdbcTemplate.execute("DROP PACKAGE BODY junit_utplsql_test_pkg") - } - - @Test - def void getObjectType() { - val dao = new UtplsqlDao(dataSource.connection) - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test_pkg IS - -- %suite - - -- %test - PROCEDURE p1; - END junit_utplsql_test_pkg; - ''') - val actual = dao.getObjectType("SCOTT", "JUNIT_UTPLSQL_TEST_PKG") - Assert.assertEquals("PACKAGE", actual) - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test_pkg") - } - - @Test - def void normalizedUtPlsqlVersion() { - val dao = new UtplsqlDao(dataSource.connection) - val version = dao.normalizedUtPlsqlVersion - Assert.assertTrue(version !== null) - } - -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterFetchSizeTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterFetchSizeTest.java new file mode 100644 index 00000000..94064821 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterFetchSizeTest.java @@ -0,0 +1,138 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.dal; + +import java.util.Arrays; +import java.util.UUID; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.jdbc.datasource.SingleConnectionDataSource; +import org.utplsql.sqldev.dal.RealtimeReporterDao; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.test.AbstractJdbcTest; + +public class RealtimeReporterFetchSizeTest extends AbstractJdbcTest { + private static final Logger logger = Logger.getLogger(RealtimeReporterFetchSizeTest.class.getName()); + + @Before + public void setup() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_fetch_size_pkg is\n"); + sb.append(" --%suite(JUnit testing)\n\n"); + + sb.append(" --%test(test 1 - 0 seconds)\n"); + sb.append(" PROCEDURE test_1_0;\n\n"); + + sb.append(" --%test(test 2 - 1 seconds)\n"); + sb.append(" PROCEDURE test_2_1;\n\n"); + + sb.append(" --%test(test 3 - 2 seconds)\n"); + sb.append(" PROCEDURE test_3_2;\n\n"); + + sb.append(" --%test(test 4 - 0 seconds)\n"); + sb.append(" PROCEDURE test_4_0;\n\n"); + + sb.append(" --%test(test 5 - 0 seconds)\n"); + sb.append(" PROCEDURE test_5_0;\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + sb.setLength(0); + sb.append("CREATE OR REPLACE PACKAGE BODY junit_utplsql_fetch_size_pkg is\n"); + sb.append(" PROCEDURE test_1_0 IS\n"); + sb.append(" BEGIN\n"); + sb.append(" NULL;\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_2_1 IS\n"); + sb.append(" BEGIN\n"); + sb.append(" dbms_session.sleep(1);\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_3_2 IS\n"); + sb.append(" BEGIN\n"); + sb.append(" dbms_session.sleep(2);\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_4_0 IS\n"); + sb.append(" BEGIN\n"); + sb.append(" NULL;\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_5_0 IS\n"); + sb.append(" BEGIN\n"); + sb.append(" NULL;\n"); + sb.append(" END;\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + } + + @After + public void teardown() { + executeAndIgnore(jdbcTemplate, "DROP PACKAGE junit_utplsql_fetch_size_pkg"); + } + + private void delayFreeStreamingConsumtionProducer(final String reporterId) { + SingleConnectionDataSource ds = new SingleConnectionDataSource(); + ds.setDriverClassName("oracle.jdbc.OracleDriver"); + ds.setUrl(dataSource.getUrl()); + ds.setUsername(dataSource.getUsername()); + ds.setPassword(dataSource.getPassword()); + final RealtimeReporterDao dao = new RealtimeReporterDao(DatabaseTools.getConnection(ds)); + dao.produceReport(reporterId, Arrays.asList("junit_utplsql_fetch_size_pkg")); + } + + @Test + public void delayFreeStreamingConsumtion() { + final long TOLERANCE_MS = 600; + SingleConnectionDataSource ds = new SingleConnectionDataSource(); + ds.setDriverClassName("oracle.jdbc.OracleDriver"); + ds.setUrl(dataSource.getUrl()); + ds.setUsername(dataSource.getUsername()); + ds.setPassword(dataSource.getPassword()); + final TestRealtimerReporterEventTimedConsumer consumer = new TestRealtimerReporterEventTimedConsumer(); + final String reporterId = UUID.randomUUID().toString().replace("-", ""); + final RealtimeReporterDao dao = new RealtimeReporterDao(DatabaseTools.getConnection(ds)); + final Thread thread = new Thread(() -> delayFreeStreamingConsumtionProducer(reporterId)); + thread.setName("utPLSQL run test"); + thread.start(); + dao.consumeReport(reporterId, consumer); + logger.fine(consumer.getPostTestEvents().toString()); + Assert.assertEquals(5, consumer.getPostTestEvents().entrySet().size()); + final Long test_1_0 = consumer.getPostTestEvents().get("junit_utplsql_fetch_size_pkg.test_1_0"); + final Long test_2_1 = consumer.getPostTestEvents().get("junit_utplsql_fetch_size_pkg.test_2_1"); + final Long test_3_2 = consumer.getPostTestEvents().get("junit_utplsql_fetch_size_pkg.test_3_2"); + final Long test_4_0 = consumer.getPostTestEvents().get("junit_utplsql_fetch_size_pkg.test_4_0"); + final Long test_5_0 = consumer.getPostTestEvents().get("junit_utplsql_fetch_size_pkg.test_5_0"); + final long test_2_1_time = test_2_1 - test_1_0; + logger.fine("test_2_1 time [ms]: " + test_2_1_time); + Assert.assertTrue("test_2_1 runtime was too long", test_2_1_time < 1000 + TOLERANCE_MS); + Assert.assertTrue("test_2_1 runtime was too short", test_2_1_time > 1000 - TOLERANCE_MS); + final long test_3_2_time = test_3_2 - test_2_1; + logger.fine("test_3_2 time [ms]: " + test_3_2_time); + Assert.assertTrue("test_3_2 runtime was too long", test_3_2_time < 2000 + TOLERANCE_MS); + Assert.assertTrue("test_3_2 runtime was too short", test_3_2_time > 2000 - TOLERANCE_MS); + final long test_4_0_time = test_4_0 - test_3_2; + logger.fine("test_4_0 time [ms]: " + test_4_0_time); + Assert.assertTrue("test_4_0 runtime was too long", test_4_0_time < TOLERANCE_MS); + final long test_5_0_time = test_5_0 - test_4_0; + logger.fine("test_5_0 time [ms]: " + test_5_0_time); + Assert.assertTrue("test_5_0 runtime was too long", test_5_0_time < TOLERANCE_MS); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterFetchSizeTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterFetchSizeTest.xtend deleted file mode 100644 index 0bf3f7d9..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterFetchSizeTest.xtend +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.utplsql.sqldev.test.dal - -import java.util.UUID -import java.util.logging.Logger -import org.junit.AfterClass -import org.junit.Assert -import org.junit.BeforeClass -import org.junit.Test -import org.springframework.jdbc.BadSqlGrammarException -import org.springframework.jdbc.datasource.SingleConnectionDataSource -import org.utplsql.sqldev.dal.RealtimeReporterDao -import org.utplsql.sqldev.test.AbstractJdbcTest - -class RealtimeReporterFetchSizeTest extends AbstractJdbcTest { - - static val Logger logger = Logger.getLogger(RealtimeReporterFetchSizeTest.name); - - @BeforeClass - def static void setup() { - - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_fetch_size_pkg is - --%suite(JUnit testing) - - --%test(test 1 - 0 seconds) - PROCEDURE test_1_0; - - --%test(test 2 - 1 seconds) - PROCEDURE test_2_1; - - --%test(test 3 - 2 seconds) - PROCEDURE test_3_2; - - --%test(test 4 - 0 seconds) - PROCEDURE test_4_0; - - --%test(test 5 - 0 seconds) - PROCEDURE test_5_0; - END; - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE BODY junit_utplsql_fetch_size_pkg is - PROCEDURE test_1_0 IS - BEGIN - NULL; - END; - - PROCEDURE test_2_1 IS - BEGIN - dbms_session.sleep(1); - END; - - PROCEDURE test_3_2 IS - BEGIN - dbms_session.sleep(2); - END; - - PROCEDURE test_4_0 IS - BEGIN - NULL; - END; - - PROCEDURE test_5_0 IS - BEGIN - NULL; - END; - END; - ''') - } - - @AfterClass - def static void teardown() { - try { - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_fetch_size_pkg") - } catch (BadSqlGrammarException e) { - // ignore - } - } - - private def delayFreeStreamingConsumtionProducer(String reporterId) { - var ds = new SingleConnectionDataSource() - ds.driverClassName = "oracle.jdbc.OracleDriver" - ds.url = dataSource.url - ds.username = dataSource.username - ds.password = dataSource.password - val dao = new RealtimeReporterDao(ds.connection) - dao.produceReport(reporterId, #["junit_utplsql_fetch_size_pkg"]) - } - - @Test - def void delayFreeStreamingConsumtion() { - val long TOLERANCE_MS = 600 - var ds = new SingleConnectionDataSource() - ds.driverClassName = "oracle.jdbc.OracleDriver" - ds.url = dataSource.url - ds.username = dataSource.username - ds.password = dataSource.password - val consumer = new TestRealtimerReporterEventTimedConsumer - val reporterId = UUID.randomUUID().toString.replace("-", ""); - val dao = new RealtimeReporterDao(ds.connection) - val Runnable runnable = [|delayFreeStreamingConsumtionProducer(reporterId)] - val thread = new Thread(runnable) - thread.name = "utPLSQL run test" - thread.start - dao.consumeReport(reporterId, consumer) - logger.fine(consumer.postTestEvents.toString) - Assert.assertEquals(5, consumer.postTestEvents.entrySet.size) - val test_1_0 = consumer.postTestEvents.get("junit_utplsql_fetch_size_pkg.test_1_0") - val test_2_1 = consumer.postTestEvents.get("junit_utplsql_fetch_size_pkg.test_2_1") - val test_3_2 = consumer.postTestEvents.get("junit_utplsql_fetch_size_pkg.test_3_2") - val test_4_0 = consumer.postTestEvents.get("junit_utplsql_fetch_size_pkg.test_4_0") - val test_5_0 = consumer.postTestEvents.get("junit_utplsql_fetch_size_pkg.test_5_0") - val test_2_1_time = test_2_1 - test_1_0 - logger.fine("test_2_1 time [ms]: " + test_2_1_time) - Assert.assertTrue("test_2_1 runtime was too long", test_2_1_time < 1000 + TOLERANCE_MS) - Assert.assertTrue("test_2_1 runtime was too short", test_2_1_time > 1000 - TOLERANCE_MS) - val test_3_2_time = test_3_2 - test_2_1 - logger.fine("test_3_2 time [ms]: " + test_3_2_time) - Assert.assertTrue("test_3_2 runtime was too long", test_3_2_time < 2000 + TOLERANCE_MS) - Assert.assertTrue("test_3_2 runtime was too short", test_3_2_time > 2000 - TOLERANCE_MS) - val test_4_0_time = test_4_0 - test_3_2 - logger.fine("test_4_0 time [ms]: " + test_4_0_time) - Assert.assertTrue("test_4_0 runtime was too long", test_4_0_time < TOLERANCE_MS) - val test_5_0_time = test_5_0 - test_4_0 - logger.fine("test_5_0 time [ms]: " + test_5_0_time) - Assert.assertTrue("test_5_0 runtime was too long", test_5_0_time < TOLERANCE_MS) - } - -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterTest.java new file mode 100644 index 00000000..ee975abd --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterTest.java @@ -0,0 +1,171 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.dal; + +import java.util.Arrays; +import java.util.UUID; +import java.util.logging.Logger; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.utplsql.sqldev.dal.RealtimeReporterDao; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.runner.PostRunEvent; +import org.utplsql.sqldev.model.runner.PostSuiteEvent; +import org.utplsql.sqldev.model.runner.PostTestEvent; +import org.utplsql.sqldev.model.runner.PreRunEvent; +import org.utplsql.sqldev.model.runner.PreSuiteEvent; +import org.utplsql.sqldev.model.runner.PreTestEvent; +import org.utplsql.sqldev.test.AbstractJdbcTest; + +public class RealtimeReporterTest extends AbstractJdbcTest { + private static final Logger logger = Logger.getLogger(RealtimeReporterTest.class.getName()); + + @Before + public void setup() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test1_pkg is\n"); + sb.append(" --%suite(JUnit testing)\n"); + sb.append(" --%suitepath(a)\n\n"); + + sb.append(" --%context(test context)\n\n"); + + sb.append(" --%test(test 1 - OK)\n"); + sb.append(" PROCEDURE test_1_ok;\n\n"); + + sb.append(" --%test(test 2 - NOK)\n"); + sb.append(" PROCEDURE test_2_nok;\n\n"); + + sb.append(" --%endcontext\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + sb.setLength(0); + sb.append("CREATE OR REPLACE PACKAGE BODY junit_utplsql_test1_pkg IS\n"); + sb.append(" PROCEDURE test_1_ok IS\n"); + sb.append(" BEGIN\n"); + sb.append(" ut.expect(1).to_equal(1);\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_2_nok IS\n"); + sb.append(" BEGIN\n"); + sb.append(" ut.expect(1).to_equal(2);\n"); + sb.append(" END;\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + sb.setLength(0); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test2_pkg IS\n"); + sb.append(" --%suite\n"); + sb.append(" --%suitepath(b)\n\n"); + + sb.append(" --%test\n"); + sb.append(" PROCEDURE test_3_ok;\n\n"); + + sb.append(" --%test\n"); + sb.append(" PROCEDURE test_4_nok;\n\n"); + + sb.append(" --%test\n"); + sb.append(" --%disabled\n"); + sb.append(" PROCEDURE test_5;\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + sb.setLength(0); + sb.append("CREATE OR REPLACE PACKAGE BODY junit_utplsql_test2_pkg IS\n"); + sb.append(" PROCEDURE test_3_ok IS\n"); + sb.append(" BEGIN\n"); + sb.append(" ut3.ut.expect(2).to_equal(2);\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_4_nok IS\n"); + sb.append(" BEGIN\n"); + sb.append(" ut3.ut.expect(2).to_equal(3);\n"); + sb.append(" ut3.ut.expect(2).to_equal(4);\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_5 IS\n"); + sb.append(" BEGIN\n"); + sb.append(" NULL;\n"); + sb.append(" END;\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + sb.setLength(0); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test3_pkg IS\n"); + sb.append(" --%suite\n"); + sb.append(" --%suitepath(b)\n\n"); + + sb.append(" --%test\n"); + sb.append(" PROCEDURE test_6_with_runtime_error;\n\n"); + + sb.append(" --%test\n"); + sb.append(" PROCEDURE test_7_with_serveroutput;\n\n"); + + sb.append(" --%afterall\n"); + sb.append(" PROCEDURE print_and_raise;\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + sb.setLength(0); + sb.append("CREATE OR REPLACE PACKAGE BODY junit_utplsql_test3_pkg IS\n"); + sb.append(" PROCEDURE test_6_with_runtime_error IS\n"); + sb.append(" l_actual INTEGER;\n"); + sb.append(" BEGIN\n"); + sb.append(" EXECUTE IMMEDIATE 'select 6 from non_existing_table' INTO l_actual;\n"); + sb.append(" ut3.ut.expect(6).to_equal(l_actual);\n"); + sb.append(" END\n\n;"); + + sb.append(" PROCEDURE test_7_with_serveroutput IS\n"); + sb.append(" BEGIN\n"); + sb.append(" dbms_output.put_line('before test 7');\n"); + sb.append(" ut3.ut.expect(7).to_equal(7);\n"); + sb.append(" dbms_output.put_line('after test 7');\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE print_and_raise IS\n"); + sb.append(" BEGIN\n"); + sb.append(" dbms_output.put_line('Now, a no_data_found exception is raised');\n"); + sb.append(" dbms_output.put_line('dbms_output and error stack is reported for this suite.');\n"); + sb.append(" dbms_output.put_line('A runtime error in afterall is counted as a warning.');\n"); + sb.append(" RAISE no_data_found;\n"); + sb.append(" END;\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + } + + @After + public void teardown() { + executeAndIgnore(jdbcTemplate, "DROP PACKAGE junit_utplsql_test1_pkg"); + executeAndIgnore(jdbcTemplate, "DROP PACKAGE junit_utplsql_test2_pkg"); + executeAndIgnore(jdbcTemplate, "DROP PACKAGE junit_utplsql_test3_pkg"); + } + + @Test + public void produceAndConsume() { + final RealtimeReporterDao dao = new RealtimeReporterDao(DatabaseTools.getConnection(dataSource)); + final String reporterId = UUID.randomUUID().toString().replace("-", ""); + final TestRealtimerReporterEventConsumer consumer = new TestRealtimerReporterEventConsumer(); + dao.produceReport(reporterId, Arrays.asList(":a", ":b")); + dao.consumeReport(reporterId, consumer); + logger.fine(consumer.getConsumedList().toString()); + Assert.assertEquals(1, consumer.getConsumedList().stream().filter(it -> it instanceof PreRunEvent).count()); + Assert.assertEquals(1, consumer.getConsumedList().stream().filter(it -> it instanceof PostRunEvent).count()); + // 2 suitepaths (a, b), 1 context, 3 packages -> 6 suites + Assert.assertEquals(6, consumer.getConsumedList().stream().filter(it -> it instanceof PreSuiteEvent).count()); + Assert.assertEquals(6, consumer.getConsumedList().stream().filter(it -> it instanceof PostSuiteEvent).count()); + Assert.assertEquals(7, consumer.getConsumedList().stream().filter(it -> it instanceof PreTestEvent).count()); + Assert.assertEquals(7, consumer.getConsumedList().stream().filter(it -> it instanceof PostTestEvent).count()); + Assert.assertEquals(28, consumer.getConsumedList().size()); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterTest.xtend deleted file mode 100644 index 908fe3f2..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterTest.xtend +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.utplsql.sqldev.test.dal - -import java.util.UUID -import java.util.logging.Logger -import org.junit.AfterClass -import org.junit.Assert -import org.junit.BeforeClass -import org.junit.Test -import org.springframework.jdbc.BadSqlGrammarException -import org.utplsql.sqldev.dal.RealtimeReporterDao -import org.utplsql.sqldev.model.runner.PostRunEvent -import org.utplsql.sqldev.model.runner.PostSuiteEvent -import org.utplsql.sqldev.model.runner.PostTestEvent -import org.utplsql.sqldev.model.runner.PreRunEvent -import org.utplsql.sqldev.model.runner.PreSuiteEvent -import org.utplsql.sqldev.model.runner.PreTestEvent -import org.utplsql.sqldev.test.AbstractJdbcTest - -class RealtimeReporterTest extends AbstractJdbcTest { - - static val Logger logger = Logger.getLogger(RealtimeReporterTest.name); - - @BeforeClass - def static void setup() { - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test1_pkg is - --%suite(JUnit testing) - --%suitepath(a) - - --%context(test context) - - --%test(test 1 - OK) - PROCEDURE test_1_ok; - - --%test(test 2 - NOK) - PROCEDURE test_2_nok; - - --%endcontext - END; - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE BODY junit_utplsql_test1_pkg IS - PROCEDURE test_1_ok IS - BEGIN - ut.expect(1).to_equal(1); - END; - - PROCEDURE test_2_nok IS - BEGIN - ut.expect(1).to_equal(2); - END; - END; - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test2_pkg IS - --%suite - --%suitepath(b) - - --%test - PROCEDURE test_3_ok; - - --%test - PROCEDURE test_4_nok; - - --%test - --%disabled - PROCEDURE test_5; - end; - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE BODY junit_utplsql_test2_pkg IS - PROCEDURE test_3_ok IS - BEGIN - ut3.ut.expect(2).to_equal(2); - END; - - PROCEDURE test_4_nok IS - BEGIN - ut3.ut.expect(2).to_equal(3); - ut3.ut.expect(2).to_equal(4); - END; - - PROCEDURE test_5 IS - BEGIN - null; - END; - END; - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test3_pkg IS - --%suite - --%suitepath(b) - - --%test - PROCEDURE test_6_with_runtime_error; - - --%test - PROCEDURE test_7_with_serveroutput; - - --%afterall - PROCEDURE print_and_raise; - END; - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE BODY junit_utplsql_test3_pkg IS - PROCEDURE test_6_with_runtime_error is - l_actual INTEGER; - BEGIN - EXECUTE IMMEDIATE 'select 6 from non_existing_table' INTO l_actual; - ut3.ut.expect(6).to_equal(l_actual); - END; - - PROCEDURE test_7_with_serveroutput IS - BEGIN - dbms_output.put_line('before test 7'); - ut3.ut.expect(7).to_equal(7); - dbms_output.put_line('after test 7'); - END; - - PROCEDURE print_and_raise IS - BEGIN - dbms_output.put_line('Now, a no_data_found exception is raised'); - dbms_output.put_line('dbms_output and error stack is reported for this suite.'); - dbms_output.put_line('A runtime error in afterall is counted as a warning.'); - raise no_data_found; - END; - END; - ''') - } - - @AfterClass - def static void teardown() { - try { - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test1_pkg") - } catch (BadSqlGrammarException e) { - // ignore - } - try { - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test2_pkg") - } catch (BadSqlGrammarException e) { - // ignore - } - try { - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test3_pkg") - } catch (BadSqlGrammarException e) { - // ignore - } - } - - @Test - def void produceAndConsume() { - val dao = new RealtimeReporterDao(dataSource.connection) - val reporterId = UUID.randomUUID().toString.replace("-", ""); - val consumer = new TestRealtimerReporterEventConsumer - dao.produceReport(reporterId, #[":a", ":b"]) - dao.consumeReport(reporterId, consumer) - logger.fine(consumer.consumedList.toString) - Assert.assertEquals(1, consumer.consumedList.filter[it instanceof PreRunEvent].size) - Assert.assertEquals(1, consumer.consumedList.filter[it instanceof PostRunEvent].size) - // 2 suitepaths (a, b), 1 context, 3 packages -> 6 suites - Assert.assertEquals(6, consumer.consumedList.filter[it instanceof PreSuiteEvent].size) - Assert.assertEquals(6, consumer.consumedList.filter[it instanceof PostSuiteEvent].size) - Assert.assertEquals(7, consumer.consumedList.filter[it instanceof PreTestEvent].size) - Assert.assertEquals(7, consumer.consumedList.filter[it instanceof PostTestEvent].size) - Assert.assertEquals(28, consumer.consumedList.size) - } - -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/TestRealtimerReporterEventConsumer.java b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/TestRealtimerReporterEventConsumer.java new file mode 100644 index 00000000..da1c4c90 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/TestRealtimerReporterEventConsumer.java @@ -0,0 +1,34 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.dal; + +import java.util.ArrayList; + +import org.utplsql.sqldev.dal.RealtimeReporterEventConsumer; +import org.utplsql.sqldev.model.runner.RealtimeReporterEvent; + +public class TestRealtimerReporterEventConsumer implements RealtimeReporterEventConsumer { + private final ArrayList consumedList = new ArrayList<>(); + + public ArrayList getConsumedList() { + return consumedList; + } + + @Override + public void process(final RealtimeReporterEvent event) { + consumedList.add(event); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/TestRealtimerReporterEventConsumer.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/TestRealtimerReporterEventConsumer.xtend deleted file mode 100644 index caa75d71..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/TestRealtimerReporterEventConsumer.xtend +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test.dal - -import java.util.ArrayList -import org.utplsql.sqldev.dal.RealtimeReporterEventConsumer -import org.utplsql.sqldev.model.runner.RealtimeReporterEvent - -class TestRealtimerReporterEventConsumer implements RealtimeReporterEventConsumer { - - val consumedList = new ArrayList - - def getConsumedList() { - return consumedList - } - - override void process(RealtimeReporterEvent event) { - consumedList.add(event) - } - -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/TestRealtimerReporterEventTimedConsumer.java b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/TestRealtimerReporterEventTimedConsumer.java new file mode 100644 index 00000000..e08ba728 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/TestRealtimerReporterEventTimedConsumer.java @@ -0,0 +1,37 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.dal; + +import java.util.HashMap; + +import org.utplsql.sqldev.dal.RealtimeReporterEventConsumer; +import org.utplsql.sqldev.model.runner.PostTestEvent; +import org.utplsql.sqldev.model.runner.RealtimeReporterEvent; + +public class TestRealtimerReporterEventTimedConsumer implements RealtimeReporterEventConsumer { + private final HashMap postTestEvents = new HashMap<>(); + + public HashMap getPostTestEvents() { + return postTestEvents; + } + + @Override + public void process(final RealtimeReporterEvent event) { + if (event instanceof PostTestEvent) { + postTestEvents.put(((PostTestEvent) event).getId(), System.currentTimeMillis()); + } + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/parser/SqlDevParserTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/parser/SqlDevParserTest.java new file mode 100644 index 00000000..802843e7 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/parser/SqlDevParserTest.java @@ -0,0 +1,148 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.parser; + +import java.util.Set; + +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.parser.SqlDevParser; + +import oracle.dbtools.raptor.navigator.plsql.Member; + +public class SqlDevParserTest { + + private String getPackageSpec() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test1_pkg IS\n"); + sb.append(" --%suite(JUnit testing)\n"); + sb.append(" --%suitepath(a)\n\n"); + + sb.append(" --%context(test context)\n\n"); + + sb.append(" --%test(test 1 - OK)\n"); + sb.append(" PRoCeDURE test_1_ok;\n\n"); + + sb.append(" --%test(test 2 - NOK)\n"); + sb.append(" PROCEDURE test_2_nok;\n\n"); + + sb.append(" --%test(test 3 - disabled)\n"); + sb.append(" --%disabled\n"); + sb.append(" PROCEDURE test_3_disabled;\n\n"); + + sb.append(" --%test(test 4 - errored)\n"); + sb.append(" PROCEDURE test_4_errored;\n\n"); + + sb.append(" --%test(test 5 - warnings)\n"); + sb.append(" PROCEDURE test_5_warnings;\n"); + sb.append(" --%endcontext\n\n"); + + sb.append(" function my_Func (p IN number) RETURN BOOLEAN;\n"); + sb.append("END;"); + return sb.toString(); + } + + private String getPackageBody() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE BODY junit_utplsql_test1_pkg IS\n"); + sb.append(" PROCEDURE test_1_ok IS\n"); + sb.append(" BEGIN\n"); + sb.append(" dbms_output.put_line('start test 1');\n"); + sb.append(" dbms_session.sleep(1);\n"); + sb.append(" ut.expect(1).to_equal(1);\n"); + sb.append(" dbms_output.put_line('end test 1');\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_2_nok IS\n"); + sb.append(" BEGIN\n"); + sb.append(" dbms_output.put_line('start test 2');\n"); + sb.append(" dbms_session.sleep(2);\n"); + sb.append(" ut.expect(1, 'first assert.').to_equal(2);\n"); + sb.append(" ut.expect(1, 'second assert.').to_equal(2);\n"); + sb.append(" dbms_output.put_line('end test 2');\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_3_disabled IS\n"); + sb.append(" BEGIN\n"); + sb.append(" NULL;\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_4_errored IS\n"); + sb.append(" BEGIN\n"); + sb.append(" EXECUTE IMMEDIATE 'bla bla';\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_5_warnings IS\n"); + sb.append(" BEGIN\n"); + sb.append(" COMMIT; -- will raise a warning\n"); + sb.append(" ut.expect(1).to_equal(1);\n"); + sb.append(" END;\n\n"); + + sb.append(" FUNCTION my_Func (p IN number) RETURN BOOLEAN IS\n"); + sb.append(" RETURN TRUE;\n"); + sb.append(" END;\n"); + sb.append("END;"); + return sb.toString(); + } + + @Test + public void packageSpecMembers() { + final SqlDevParser parser = new SqlDevParser(); + final Set actual = parser.getMembers(getPackageSpec()); + Assert.assertEquals(6, actual.size()); + final Member first = actual.stream().findFirst().orElse(null); + Assert.assertNotNull(first); + Assert.assertEquals("PROCEDURE", first.type); + Assert.assertEquals("test_1_ok", first.name); + final Member last = actual.stream().reduce((m1, m2) -> m2).orElse(null); + Assert.assertNotNull(last); + Assert.assertEquals("FUNCTION", last.type); + Assert.assertEquals("my_Func", last.name); + } + + @Test + public void packageBodyMembers() { + final SqlDevParser parser = new SqlDevParser(); + final Set actual = parser.getMembers(getPackageBody()); + Assert.assertEquals(6, actual.size()); + final Member first = actual.stream().findFirst().orElse(null); + Assert.assertNotNull(first); + Assert.assertEquals("PROCEDURE", first.type); + Assert.assertEquals("test_1_ok", first.name); + final Member last = actual.stream().reduce((m1, m2) -> m2).orElse(null); + Assert.assertNotNull(last); + Assert.assertEquals("FUNCTION", last.type); + Assert.assertEquals("my_Func", last.name); + } + + @Test + public void StartLineSpec() { + final SqlDevParser parser = new SqlDevParser(); + final int first = parser.getMemberStartLine(getPackageSpec(), "test_1_ok"); + Assert.assertEquals(8, first); + final int last = parser.getMemberStartLine(getPackageSpec(), "my_func"); + Assert.assertEquals(24, last); + } + + @Test + public void StartLineBody() { + final SqlDevParser parser = new SqlDevParser(); + final int first = parser.getMemberStartLine(getPackageBody(), "test_1_ok"); + Assert.assertEquals(2, first); + final int last = parser.getMemberStartLine(getPackageBody(), "my_func"); + Assert.assertEquals(35, last); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/parser/SqlDevParserTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/parser/SqlDevParserTest.xtend deleted file mode 100644 index a424e7d2..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/parser/SqlDevParserTest.xtend +++ /dev/null @@ -1,123 +0,0 @@ -package org.utplsql.sqldev.test.parser - -import org.junit.Assert -import org.junit.Test -import org.utplsql.sqldev.parser.SqlDevParser - -class SqlDevParserTest { - val packageSpec = ''' - CREATE OR REPLACE PACKAGE junit_utplsql_test1_pkg is - --%suite(JUnit testing) - --%suitepath(a) - - --%context(test context) - - --%test(test 1 - OK) - PRoCeDURE test_1_ok; - - --%test(test 2 - NOK) - PROCEDURE test_2_nok; - - --%test(test 3 - disabled) - --%disabled - PROCEDURE test_3_disabled; - - --%test(test 4 - errored) - PROCEDURE test_4_errored; - - --%test(test 5 - warnings) - PROCEDURE test_5_warnings; - --%endcontext - - function my_Func (p IN number) RETURN BOOLEAN; - END; - ''' - - val packageBody = ''' - CREATE OR REPLACE PACKAGE BODY junit_utplsql_test1_pkg IS - PROCEDURE test_1_ok IS - BEGIN - dbms_output.put_line('start test 1'); - dbms_session.sleep(1); - ut.expect(1).to_equal(1); - dbms_output.put_line('end test 1'); - END; - - PROCEDURE test_2_nok IS - BEGIN - dbms_output.put_line('start test 2'); - dbms_session.sleep(2); - ut.expect(1, 'first assert.').to_equal(2); - ut.expect(1, 'second assert.').to_equal(2); - dbms_output.put_line('end test 2'); - END; - - PROCEDURE test_3_disabled IS - BEGIN - NULL; - END; - - PROCEDURE test_4_errored IS - BEGIN - EXECUTE IMMEDIATE 'bla bla'; - END; - - PROCEDURE test_5_warnings IS - BEGIN - COMMIT; -- will raise a warning - ut.expect(1).to_equal(1); - END; - - FUNCTION my_Func (p IN number) RETURN BOOLEAN IS - RETURN TRUE; - END; - END; - ''' - - @Test - def void packageSpecMembers() { - val parser = new SqlDevParser - val actual = parser.getMembers(packageSpec) - Assert.assertEquals(6, actual.length) - val first = actual.get(0) - Assert.assertEquals("PROCEDURE", first.type) - Assert.assertEquals("test_1_ok", first.name) - val last = actual.get(5) - Assert.assertEquals("FUNCTION", last.type) - Assert.assertEquals("my_Func", last.name) - } - - @Test - def void packageBodyMembers() { - val parser = new SqlDevParser - val actual = parser.getMembers(packageBody) - Assert.assertEquals(6, actual.length) - val first = actual.get(0) - Assert.assertEquals("PROCEDURE", first.type) - Assert.assertEquals("test_1_ok", first.name) - val last = actual.get(5) - Assert.assertEquals("FUNCTION", last.type) - Assert.assertEquals("my_Func", last.name) - } - - @Test - def void StartLineSpec() { - val parser = new SqlDevParser - val first = parser.getMemberStartLine(packageSpec, 'test_1_ok') - Assert.assertEquals(8, first) - val last = parser.getMemberStartLine(packageSpec, 'my_func') - Assert.assertEquals(24, last) - } - - @Test - def void StartLineBody() { - val parser = new SqlDevParser - val first = parser.getMemberStartLine(packageBody, 'test_1_ok') - Assert.assertEquals(2, first) - val last = parser.getMemberStartLine(packageBody, 'my_func') - Assert.assertEquals(35, last) - } - - - -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/parser/UtplsqlParserBugFixTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/parser/UtplsqlParserBugFixTest.java new file mode 100644 index 00000000..5bae6026 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/parser/UtplsqlParserBugFixTest.java @@ -0,0 +1,114 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.parser; + +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.parser.UtplsqlParser; + +public class UtplsqlParserBugFixTest { + + // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/1 + @Test + public void issue1MatchingExprInStringLiterals() { + StringBuilder sb = new StringBuilder(); + sb.append("create or replace package body test_expect_not_to_be_null\n"); + sb.append("is\n"); + sb.append(" gc_object_name constant varchar2(30) := 't_not_to_be_null_test';\n"); + sb.append(" gc_nested_table_name constant varchar2(30) := 'tt_not_to_be_null_test';\n"); + sb.append(" gc_varray_name constant varchar2(30) := 'tv_not_to_be_null_test';\n\n"); + + sb.append(" procedure cleanup_expectations\n"); + sb.append(" is\n"); + sb.append(" begin\n"); + sb.append(" ut3.ut_expectation_processor.clear_expectations();\n"); + sb.append(" end;\n\n"); + + sb.append(" procedure create_types\n"); + sb.append(" is"); + sb.append(" pragma autonomous_transaction;\n"); + sb.append(" begin\n"); + sb.append(" execute immediate 'create type '||gc_object_name||' is object (dummy number)'\n;"); + sb.append(" execute immediate ' create type '||gc_nested_table_name||' is table of number';\n"); + sb.append(" execute immediate '\n"); + sb.append(" create type '||gc_varray_name||' is varray(1) of number';\n"); + sb.append(" end;\n\n"); + + sb.append(" procedure drop_types\n"); + sb.append(" is\n"); + sb.append(" pragma autonomous_transaction;\n"); + sb.append(" begin\n"); + sb.append(" execute immediate 'drop type '||gc_object_name;\n"); + sb.append(" execute immediate ' drop type '||gc_nested_table_name;\n"); + sb.append(" execute immediate '\n"); + sb.append(" drop type '||gc_varray_name;\n"); + sb.append(" end;\n\n"); + + sb.append(" procedure blob_not_null\n"); + sb.append(" is\n"); + sb.append(" begin\n"); + sb.append(" --Act\n"); + sb.append(" execute immediate expectations_helpers.unary_expectation_block('not_to_be_null', 'blob', 'to_blob(''abc'')');\n"); + sb.append(" --Assert\n"); + sb.append(" ut.expect(anydata.convertCollection(ut3.ut_expectation_processor.get_failed_expectations())).to_be_empty();\n"); + sb.append(" end;\n\n"); + + sb.append("--and so on...\n\n"); + + sb.append("end;"); + final String plsql = sb.toString(); + final UtplsqlParser parser = new UtplsqlParser(plsql); + Assert.assertEquals("test_expect_not_to_be_null.cleanup_expectations", + parser.getPathAt(parser.toPosition(7, 1))); + Assert.assertEquals("test_expect_not_to_be_null.create_types", parser.getPathAt(parser.toPosition(13, 1))); + // was: '||gc_varray_name||'.drop_types + Assert.assertEquals("test_expect_not_to_be_null.drop_types", parser.getPathAt(parser.toPosition(23, 1))); + // was: '||gc_varray_name||'.blob_not_null + Assert.assertEquals("test_expect_not_to_be_null.blob_not_null", parser.getPathAt(parser.toPosition(33, 1))); + } + + @Test + // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/7 + public void issue7WrongPositionWithWindowsLineSeparator() { + StringBuilder sb = new StringBuilder(); + sb.append("create or replace package test_expect_not_to_be_null\n"); + sb.append("is\n"); + sb.append(" --%suite(expectations - not_to_be_null)\n"); + sb.append(" --%suitepath(utplsql.core.expectations.unary)\n\n"); + + sb.append(" --%aftereach\n"); + sb.append(" procedure cleanup_expectations;\n\n"); + + sb.append(" --%beforeall\n"); + sb.append(" procedure create_types;\n\n"); + + sb.append(" --%afterallv"); + sb.append(" procedure drop_types;\n\n"); + + sb.append(" --%test(Gives success for not null blob)\n"); + sb.append(" procedure blob_not_null;\n\n"); + + sb.append(" --%test(Gives success for blob with length 0)\n"); + sb.append(" procedure blob_0_length;\n\n"); + + sb.append(" -- ...\n"); + sb.append("end test_expect_not_to_be_null;\n"); + sb.append("/\n"); + final String plsql = sb.toString(); + final UtplsqlParser parser = new UtplsqlParser(plsql); + Assert.assertEquals("test_expect_not_to_be_null.blob_not_null", parser.getPathAt(parser.toPosition(13, 26))); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/parser/UtplsqlParserBugFixTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/parser/UtplsqlParserBugFixTest.xtend deleted file mode 100644 index 3ec0c926..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/parser/UtplsqlParserBugFixTest.xtend +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test.parser - -import org.junit.Assert -import org.junit.Test -import org.utplsql.sqldev.parser.UtplsqlParser - -class UtplsqlParserBugFixTest { - - @Test - // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/1 - def issue1MatchingExprInStringLiterals() { - val plsql = ''' - create or replace package body test_expect_not_to_be_null - is - gc_object_name constant varchar2(30) := 't_not_to_be_null_test'; - gc_nested_table_name constant varchar2(30) := 'tt_not_to_be_null_test'; - gc_varray_name constant varchar2(30) := 'tv_not_to_be_null_test'; - - procedure cleanup_expectations - is - begin - ut3.ut_expectation_processor.clear_expectations(); - end; - - procedure create_types - is - pragma autonomous_transaction; - begin - execute immediate 'create type '||gc_object_name||' is object (dummy number)'; - execute immediate ' create type '||gc_nested_table_name||' is table of number'; - execute immediate ' - create type '||gc_varray_name||' is varray(1) of number'; - end; - - procedure drop_types - is - pragma autonomous_transaction; - begin - execute immediate 'drop type '||gc_object_name; - execute immediate ' drop type '||gc_nested_table_name; - execute immediate ' - drop type '||gc_varray_name; - end; - - procedure blob_not_null - is - begin - --Act - execute immediate expectations_helpers.unary_expectation_block('not_to_be_null', 'blob', 'to_blob(''abc'')'); - --Assert - ut.expect(anydata.convertCollection(ut3.ut_expectation_processor.get_failed_expectations())).to_be_empty(); - end; - - --and so on... - - end; - ''' - val parser = new UtplsqlParser(plsql) - Assert.assertEquals("test_expect_not_to_be_null.cleanup_expectations", parser.getPathAt(parser.toPosition(7,1))) - Assert.assertEquals("test_expect_not_to_be_null.create_types", parser.getPathAt(parser.toPosition(13,1))) - // was: '||gc_varray_name||'.drop_types - Assert.assertEquals("test_expect_not_to_be_null.drop_types", parser.getPathAt(parser.toPosition(23,1))) - // was: '||gc_varray_name||'.blob_not_null - Assert.assertEquals("test_expect_not_to_be_null.blob_not_null", parser.getPathAt(parser.toPosition(33,1))) - } - - @Test - // https://github.com/utPLSQL/utPLSQL-SQLDeveloper/issues/7 - def issue7WrongPositionWithWindowsLineSeparator() { - val plsql = ''' - create or replace package test_expect_not_to_be_null - is - --%suite(expectations - not_to_be_null) - --%suitepath(utplsql.core.expectations.unary) - - --%aftereach - procedure cleanup_expectations; - - --%beforeall - procedure create_types; - - --%afterall - procedure drop_types; - - --%test(Gives success for not null blob) - procedure blob_not_null; - - --%test(Gives success for blob with length 0) - procedure blob_0_length; - - -- ... - end test_expect_not_to_be_null; - / - ''' - val parser = new UtplsqlParser(plsql) - // was: test_expect_not_to_be_null.create_types - Assert.assertEquals("test_expect_not_to_be_null.blob_not_null", parser.getPathAt(parser.toPosition(13,26))) - } - -} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/parser/UtplsqlParserTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/parser/UtplsqlParserTest.java new file mode 100644 index 00000000..5694eb3f --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/parser/UtplsqlParserTest.java @@ -0,0 +1,299 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.parser; + +import java.util.List; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.parser.PlsqlObject; +import org.utplsql.sqldev.model.parser.Unit; +import org.utplsql.sqldev.parser.UtplsqlParser; +import org.utplsql.sqldev.test.AbstractJdbcTest; + +public class UtplsqlParserTest extends AbstractJdbcTest { + + private String getSqlScript() { + StringBuilder sb = new StringBuilder(); + sb.append("PROMPT\n"); + sb.append("PROMPT Install utPLSQL test package\n"); + sb.append("PROMPT\n\n"); + + sb.append("/*\n"); + sb.append(" * some comment\n"); + sb.append(" */\n"); + sb.append("CREATE OR REPLACE PACKAGE pkg IS\n"); + sb.append(" -- %suite\n"); + sb.append(" -- %rollback(manual)\n\n"); + + sb.append(" -- %test\n"); + sb.append(" PROCEDURE p (in_p1 INTEGER);\n"); + sb.append(" FUNCTION f (in_p1 INTEGER) RETURN INTEGER;\n"); + sb.append("END pkg;\n"); + sb.append("/\n"); + sb.append("SHOW ERRORS\n\n"); + + sb.append("CREATE OR REPLACE PACKAGE BODY \"SCOTT\".\"PKG\" IS\n"); + sb.append(" PROCEDURE \"P\" (in_p1 INTEGER) IS\n"); + sb.append(" BEGIN\n"); + sb.append(" NULL;\n"); + sb.append(" END p;\n\n"); + + sb.append(" /* comment 1 */\n"); + sb.append(" -- comment 2\n"); + sb.append(" /* comment 3 */\n"); + sb.append(" -- comment 4\n\n"); + + sb.append(" FUNCTION \"F\" (in_p1 INTEGER) RETURN INTEGER IS\n"); + sb.append(" BEGIN\n"); + sb.append(" RETURN 1;\n"); + sb.append(" END f;\n"); + sb.append("END pkg;\n"); + sb.append("/\n"); + sb.append("SHOW ERRORS\n"); + return sb.toString(); + } + + @Before + @After + public void setupAndTeardown() { + executeAndIgnore(jdbcTemplate, "DROP PACKAGE pkg"); + } + + @Test + public void packageWithoutConnection() { + final UtplsqlParser parser = new UtplsqlParser(getSqlScript()); + final List objects = parser.getObjects(); + Assert.assertEquals(2, objects.size()); + Assert.assertEquals("pkg", objects.get(0).getName()); + Assert.assertEquals("\"SCOTT\".\"PKG\"", objects.get(1).getName()); + Assert.assertTrue(objects.get(0).getPosition() < objects.get(1).getPosition()); + final List units = parser.getUnits(); + Assert.assertEquals(2, units.size()); + Assert.assertEquals("p", units.get(0).getName()); + Assert.assertEquals("\"P\"", units.get(1).getName()); + Assert.assertTrue(units.get(0).getPosition() < units.get(1).getPosition()); + Assert.assertEquals("", parser.getPathAt(0)); + Assert.assertEquals("", parser.getPathAt(parser.toPosition(3, 6))); + Assert.assertEquals("pkg", parser.getPathAt(parser.toPosition(4, 1))); + Assert.assertEquals("pkg.p", parser.getPathAt(parser.toPosition(10, 33))); + Assert.assertEquals("pkg.p", parser.getPathAt(parser.toPosition(13, 1))); + Assert.assertEquals("SCOTT.PKG.p", parser.getPathAt(parser.toPosition(19, 1))); + Assert.assertEquals("SCOTT.PKG.P", parser.getPathAt(parser.toPosition(22, 9))); + Assert.assertEquals("SCOTT.PKG.P", parser.getPathAt(parser.toPosition(22, 10))); + Assert.assertEquals("SCOTT.PKG.P", parser.getPathAt(parser.toPosition(29, 1))); + } + + @Test + public void packageWithConnection() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE pkg IS\n"); + sb.append(" -- %suite\n"); + sb.append(" -- %rollback(manual)\n\n"); + + sb.append(" -- %test\n"); + sb.append(" PROCEDURE p (in_p1 INTEGER);\n"); + sb.append(" FUNCTION f (in_p1 INTEGER) RETURN INTEGER;\n"); + sb.append("END pkg;"); + final String plsql = sb.toString(); + UtplsqlParser parser = new UtplsqlParser(plsql, DatabaseTools.getConnection(dataSource), null); + Assert.assertEquals(0, parser.getObjects().size()); + Assert.assertEquals(0, parser.getUnits().size()); + + jdbcTemplate.execute(plsql); + parser = new UtplsqlParser(plsql, DatabaseTools.getConnection(dataSource), null); + Assert.assertEquals(1, parser.getObjects().size()); + Assert.assertEquals(1, parser.getUnits().size()); + + for (final String stmt : getStatements(getSqlScript())) { + jdbcTemplate.execute(stmt); + } + parser = new UtplsqlParser(getSqlScript(), DatabaseTools.getConnection(dataSource), null); + Assert.assertEquals(2, parser.getObjects().size()); + Assert.assertEquals(2, parser.getUnits().size()); + Assert.assertEquals("pkg.p", parser.getPathAt(parser.toPosition(13, 1))); + Assert.assertEquals("SCOTT.PKG.p", parser.getPathAt(parser.toPosition(19, 1))); + } + + @Test + public void procedure() { + StringBuilder sb = new StringBuilder(); + sb.append("create or replace procedure z\n"); + sb.append("is\n"); + sb.append(" null;\n"); + sb.append("end;\n"); + sb.append("/\n"); + final String plsql = sb.toString(); + final UtplsqlParser parser = new UtplsqlParser(plsql); + Assert.assertEquals("z", parser.getObjectAt(0).getName()); + Assert.assertEquals("PROCEDURE", parser.getObjectAt(0).getType()); + } + + @Test + public void function() { + StringBuilder sb = new StringBuilder(); + sb.append("create or replace procedure z\n"); + sb.append("is\n"); + sb.append(" null;\n"); + sb.append("end;\n"); + sb.append("/\n\n"); + + sb.append("create or replace function f return number is\n"); + sb.append("begin\n"); + sb.append(" null;\n"); + sb.append("end;\n"); + sb.append("/\n"); + final String plsql = sb.toString(); + final UtplsqlParser parser = new UtplsqlParser(plsql); + Assert.assertEquals("f", parser.getObjectAt(parser.toPosition(8, 1)).getName()); + Assert.assertEquals("FUNCTION", parser.getObjectAt(parser.toPosition(8, 1)).getType()); + } + + @Test + public void type() { + StringBuilder sb = new StringBuilder(); + sb.append("create or replace type t force is\n"); + sb.append(" object (\n"); + sb.append(" a number,\n"); + sb.append(" b number,\n"); + sb.append(" c varchar2(10),\n"); + sb.append(" member procedure p(self in t)\n"); + sb.append(" )\n"); + sb.append("end;\n"); + sb.append("/\n"); + final String plsql = sb.toString(); + final UtplsqlParser parser = new UtplsqlParser(plsql); + Assert.assertEquals("t", parser.getObjectAt(0).getName()); + Assert.assertEquals("TYPE", parser.getObjectAt(0).getType()); + } + + @Test + public void typeBody() { + StringBuilder sb = new StringBuilder(); + sb.append("create or replace type body t force is\n"); + sb.append(" member procedure p(self in t) is\n"); + sb.append(" begin\n"); + sb.append(" null;\n"); + sb.append(" end;\n"); + sb.append("end;\n"); + sb.append("/\n"); + final String plsql = sb.toString(); + final UtplsqlParser parser = new UtplsqlParser(plsql); + Assert.assertEquals("t", parser.getObjectAt(0).getName()); + Assert.assertEquals("TYPE", parser.getObjectAt(0).getType()); + } + + @Test + public void unknown() { + StringBuilder sb = new StringBuilder(); + sb.append("create or replace unknown u is\n"); + sb.append("begin\n"); + sb.append(" null;\n"); + sb.append("end;\n"); + sb.append("/\n"); + final String plsql = sb.toString(); + final UtplsqlParser parser = new UtplsqlParser(plsql); + Assert.assertEquals(null, parser.getObjectAt(0)); + } + + @Test + public void StartLineSpec() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test1_pkg is\n"); + sb.append(" --%suite(JUnit testing)\n"); + sb.append(" --%suitepath(a)\n\n"); + + sb.append(" --%context(test context)\n\n"); + + sb.append(" --%test(test 1 - OK)\n"); + sb.append(" PRoCeDURE test_1_ok;\n\n"); + + sb.append(" --%test(test 2 - NOK)\n"); + sb.append(" PROCEDURE test_2_nok;\n\n"); + + sb.append(" --%test(test 3 - disabled)\n"); + sb.append(" --%disabled\n"); + sb.append(" PROCEDURE test_3_disabled;\n\n"); + + sb.append(" --%test(test 4 - errored)\n"); + sb.append(" PROCEDURE test_4_errored;\n\n"); + + sb.append(" --%test(test 5 - warnings)\n"); + sb.append(" PROCEDURE test_5_warnings;\n"); + sb.append(" --%endcontext\n\n"); + + sb.append(" function my_Func (p IN number) RETURN BOOLEAN;\n"); + sb.append("END;"); + final String plsql = sb.toString(); + final UtplsqlParser parser = new UtplsqlParser(plsql); + final int first = parser.getLineOf("test_1_ok"); + Assert.assertEquals(8, first); + final int last = parser.getLineOf("test_5_warnings"); + Assert.assertEquals(21, last); + } + + @Test + public void StartLineBody() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE BODY junit_utplsql_test1_pkg IS\n"); + sb.append(" PROCEDURE test_1_ok IS\n"); + sb.append(" BEGIN\n"); + sb.append(" dbms_output.put_line('start test 1');\n"); + sb.append(" dbms_session.sleep(1);\n"); + sb.append(" ut.expect(1).to_equal(1);\n"); + sb.append(" dbms_output.put_line('end test 1');\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_2_nok IS\n"); + sb.append(" BEGIN\n"); + sb.append(" dbms_output.put_line('start test 2');\n"); + sb.append(" dbms_session.sleep(2);\n"); + sb.append(" ut.expect(1, 'first assert.').to_equal(2);\n"); + sb.append(" ut.expect(1, 'second assert.').to_equal(2);\n"); + sb.append(" dbms_output.put_line('end test 2');\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_3_disabled IS\n"); + sb.append(" BEGIN\n"); + sb.append(" NULL;\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_4_errored IS\n"); + sb.append(" BEGIN\n"); + sb.append(" EXECUTE IMMEDIATE 'bla bla';\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_5_warnings IS\n"); + sb.append(" BEGIN\n"); + sb.append(" COMMIT; -- will raise a warning\n"); + sb.append(" ut.expect(1).to_equal(1);\n"); + sb.append(" END;\n\n"); + + sb.append(" FUNCTION my_Func (p IN number) RETURN BOOLEAN IS\n"); + sb.append(" RETURN TRUE;\n"); + sb.append(" END;\n"); + sb.append("END;"); + final String plsql = sb.toString(); + final UtplsqlParser parser = new UtplsqlParser(plsql); + final int first = parser.getLineOf("test_1_ok"); + Assert.assertEquals(2, first); + final int last = parser.getLineOf("test_5_warnings"); + Assert.assertEquals(29, last); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/parser/UtplsqlParserTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/parser/UtplsqlParserTest.xtend deleted file mode 100644 index b49293ac..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/parser/UtplsqlParserTest.xtend +++ /dev/null @@ -1,297 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test.parser - -import org.junit.AfterClass -import org.junit.Assert -import org.junit.BeforeClass -import org.junit.Test -import org.springframework.jdbc.BadSqlGrammarException -import org.utplsql.sqldev.parser.UtplsqlParser -import org.utplsql.sqldev.test.AbstractJdbcTest - -class UtplsqlParserTest extends AbstractJdbcTest { - - static val sqlScript = ''' - PROMPT - PROMPT Install utPLSQL test package - PROMPT - - /* - * some comment - */ - CREATE OR REPLACE PACKAGE pkg IS - -- %suite - -- %rollback(manual) - - -- %test - PROCEDURE p (in_p1 INTEGER); - FUNCTION f (in_p1 INTEGER) RETURN INTEGER; - END pkg; - / - SHOW ERRORS - - CREATE OR REPLACE PACKAGE BODY "SCOTT"."PKG" IS - PROCEDURE "P" (in_p1 INTEGER) IS - BEGIN - NULL; - END p; - - /* comment 1 */ - -- comment 2 - /* comment 3 */ - -- comment 4 - - FUNCTION "F" (in_p1 INTEGER) RETURN INTEGER IS - BEGIN - RETURN 1; - END f; - END pkg; - / - SHOW ERRORS - ''' - - @BeforeClass - @AfterClass - def static void setupAndTeardown() { - try { - jdbcTemplate.execute("DROP PACKAGE pkg") - } catch (BadSqlGrammarException e) { - // ignore - } - } - - @Test - def packageWithoutConnection() { - val parser = new UtplsqlParser(sqlScript) - val objects = parser.getObjects - Assert.assertEquals(2, objects.size) - Assert.assertEquals("pkg", objects.get(0).name) - Assert.assertEquals('''"SCOTT"."PKG"'''.toString, objects.get(1).name) - Assert.assertTrue(objects.get(0).position < objects.get(1).position) - val units = parser.getUnits - Assert.assertEquals(2, units.size) - Assert.assertEquals("p", units.get(0).name) - Assert.assertEquals('''"P"'''.toString, units.get(1).name) - Assert.assertTrue(units.get(0).position < units.get(1).position) - Assert.assertEquals("", parser.getPathAt(0)) - Assert.assertEquals("", parser.getPathAt(parser.toPosition(3,6))) - Assert.assertEquals("pkg", parser.getPathAt(parser.toPosition(4,1))) - Assert.assertEquals("pkg.p", parser.getPathAt(parser.toPosition(10,33))) - Assert.assertEquals("pkg.p", parser.getPathAt(parser.toPosition(13,1))) - Assert.assertEquals("SCOTT.PKG.p", parser.getPathAt(parser.toPosition(19,1))) - Assert.assertEquals("SCOTT.PKG.P", parser.getPathAt(parser.toPosition(22,9))) - Assert.assertEquals("SCOTT.PKG.P", parser.getPathAt(parser.toPosition(22,10))) - Assert.assertEquals("SCOTT.PKG.P", parser.getPathAt(parser.toPosition(29,1))) - } - - @Test - def packageWithConnection() { - val plsql = ''' - CREATE OR REPLACE PACKAGE pkg IS - -- %suite - -- %rollback(manual) - - -- %test - PROCEDURE p (in_p1 INTEGER); - FUNCTION f (in_p1 INTEGER) RETURN INTEGER; - END pkg; - ''' - var parser = new UtplsqlParser(plsql, dataSource.connection, null) - Assert.assertEquals(0, parser.getObjects.size) - Assert.assertEquals(0, parser.getUnits.size) - jdbcTemplate.execute(plsql) - parser = new UtplsqlParser(plsql, dataSource.connection, null) - Assert.assertEquals(1, parser.getObjects.size) - Assert.assertEquals(1, parser.getUnits.size) - for (stmt : getStatements(sqlScript)) { - jdbcTemplate.execute(stmt) - } - parser = new UtplsqlParser(sqlScript, dataSource.connection, null) - Assert.assertEquals(2, parser.getObjects.size) - Assert.assertEquals(2, parser.getUnits.size) - Assert.assertEquals("pkg.p", parser.getPathAt(parser.toPosition(13,1))) - Assert.assertEquals("SCOTT.PKG.p", parser.getPathAt(parser.toPosition(19,1))) - setupAndTeardown - } - - @Test - def procedure() { - val plsql = ''' - create or replace procedure z - is - null; - end; - / - ''' - val parser = new UtplsqlParser(plsql) - Assert.assertEquals("z", parser.getObjectAt(0).name) - Assert.assertEquals("PROCEDURE", parser.getObjectAt(0).type) - } - - @Test - def function() { - val plsql = ''' - create or replace procedure z - is - null; - end; - / - - create or replace function f return number is - begin - null; - end; - / - ''' - val parser = new UtplsqlParser(plsql) - Assert.assertEquals("f", parser.getObjectAt(parser.toPosition(8,1)).name) - Assert.assertEquals("FUNCTION", parser.getObjectAt(parser.toPosition(8,1)).type) - } - - @Test - def type() { - val plsql = ''' - create or replace type t force is - object ( - a number, - b number, - c varchar2(10), - member procedure p(self in t) - ) - end; - / - ''' - val parser = new UtplsqlParser(plsql) - Assert.assertEquals("t", parser.getObjectAt(0).name) - Assert.assertEquals("TYPE", parser.getObjectAt(0).type) - } - - @Test - def typeBody() { - val plsql = ''' - create or replace type body t force is - member procedure p(self in t) is - begin - null; - end; - end; - / - ''' - val parser = new UtplsqlParser(plsql) - Assert.assertEquals("t", parser.getObjectAt(0).name) - Assert.assertEquals("TYPE", parser.getObjectAt(0).type) - } - - @Test - def unknown() { - val plsql = ''' - create or replace unknown u is - begin - null; - end; - / - ''' - val parser = new UtplsqlParser(plsql) - Assert.assertEquals(null, parser.getObjectAt(0)) - } - - @Test - def void StartLineSpec() { - val plsql = ''' - CREATE OR REPLACE PACKAGE junit_utplsql_test1_pkg is - --%suite(JUnit testing) - --%suitepath(a) - - --%context(test context) - - --%test(test 1 - OK) - PRoCeDURE test_1_ok; - - --%test(test 2 - NOK) - PROCEDURE test_2_nok; - - --%test(test 3 - disabled) - --%disabled - PROCEDURE test_3_disabled; - - --%test(test 4 - errored) - PROCEDURE test_4_errored; - - --%test(test 5 - warnings) - PROCEDURE test_5_warnings; - --%endcontext - - function my_Func (p IN number) RETURN BOOLEAN; - END; - ''' - val parser = new UtplsqlParser(plsql) - val first = parser.getLineOf('test_1_ok') - Assert.assertEquals(8, first) - val last = parser.getLineOf('test_5_warnings') - Assert.assertEquals(21, last) - } - - @Test - def void StartLineBody() { - val plsql = ''' - CREATE OR REPLACE PACKAGE BODY junit_utplsql_test1_pkg IS - PROCEDURE test_1_ok IS - BEGIN - dbms_output.put_line('start test 1'); - dbms_session.sleep(1); - ut.expect(1).to_equal(1); - dbms_output.put_line('end test 1'); - END; - - PROCEDURE test_2_nok IS - BEGIN - dbms_output.put_line('start test 2'); - dbms_session.sleep(2); - ut.expect(1, 'first assert.').to_equal(2); - ut.expect(1, 'second assert.').to_equal(2); - dbms_output.put_line('end test 2'); - END; - - PROCEDURE test_3_disabled IS - BEGIN - NULL; - END; - - PROCEDURE test_4_errored IS - BEGIN - EXECUTE IMMEDIATE 'bla bla'; - END; - - PROCEDURE test_5_warnings IS - BEGIN - COMMIT; -- will raise a warning - ut.expect(1).to_equal(1); - END; - - FUNCTION my_Func (p IN number) RETURN BOOLEAN IS - RETURN TRUE; - END; - END; - ''' - val parser = new UtplsqlParser(plsql) - val first = parser.getLineOf('test_1_ok') - Assert.assertEquals(2, first) - val last = parser.getLineOf('test_5_warnings') - Assert.assertEquals(29, last) - } - -} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/preference/PreferenceModelTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/preference/PreferenceModelTest.java new file mode 100644 index 00000000..d246c315 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/preference/PreferenceModelTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.preference; + +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.model.preference.PreferenceModel; + +public class PreferenceModelTest { + + @Test + public void defaultValues() { + final PreferenceModel model = PreferenceModel.getInstance(null); + Assert.assertTrue(model.isUseRealtimeReporter()); + Assert.assertTrue(model.isUnsharedWorksheet()); + Assert.assertFalse(model.isResetPackage()); + Assert.assertFalse(model.isClearScreen()); + Assert.assertTrue(model.isAutoExecute()); + Assert.assertFalse(model.isCheckRunUtplsqlTest()); + Assert.assertFalse(model.isUseSmartTimes()); + Assert.assertEquals(10, model.getNumberOfRunsInHistory()); + Assert.assertFalse(model.isShowDisabledCounter()); + Assert.assertFalse(model.isShowWarningsCounter()); + Assert.assertFalse(model.isShowInfoCounter()); + Assert.assertFalse(model.isShowWarningIndicator()); + Assert.assertFalse(model.isShowInfoIndicator()); + Assert.assertTrue(model.isShowSuccessfulTests()); + Assert.assertTrue(model.isShowDisabledTests()); + Assert.assertFalse(model.isShowTestDescription()); + Assert.assertTrue(model.isSyncDetailTab()); + Assert.assertEquals("test_", model.getTestPackagePrefix()); + Assert.assertEquals("", model.getTestPackageSuffix()); + Assert.assertEquals("", model.getTestUnitPrefix()); + Assert.assertEquals("", model.getTestUnitSuffix()); + Assert.assertEquals(1, model.getNumberOfTestsPerUnit()); + Assert.assertFalse(model.isCheckGenerateUtplsqlTest()); + Assert.assertTrue(model.isGenerateComments()); + Assert.assertFalse(model.isDisableTests()); + Assert.assertEquals("alltests", model.getSuitePath()); + Assert.assertEquals(3, model.getIndentSpaces()); + Assert.assertTrue(model.isGenerateFiles()); + Assert.assertEquals(PreferenceModel.DEFAULT_OUTPUT_DIRECTORY, model.getOutputDirectory()); + Assert.assertEquals(false, model.isDeleteExistingFiles()); + Assert.assertEquals("utPLSQL", model.getRootFolderInOddgenView()); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/preference/PreferenceModelTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/preference/PreferenceModelTest.xtend deleted file mode 100644 index ca5b7142..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/preference/PreferenceModelTest.xtend +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test.preference - -import org.junit.Assert -import org.junit.Test -import org.utplsql.sqldev.model.preference.PreferenceModel - -class PreferenceModelTest { - - @Test - def defaultValues() { - val PreferenceModel model = PreferenceModel.getInstance(null) - Assert.assertTrue(model.useRealtimeReporter) - Assert.assertTrue(model.unsharedWorksheet) - Assert.assertFalse(model.resetPackage) - Assert.assertFalse(model.clearScreen) - Assert.assertTrue(model.autoExecute) - Assert.assertFalse(model.checkRunUtplsqlTest) - Assert.assertFalse(model.useSmartTimes) - Assert.assertEquals(model.numberOfRunsInHistory, 10) - Assert.assertFalse(model.showDisabledCounter) - Assert.assertFalse(model.showWarningsCounter) - Assert.assertFalse(model.showInfoCounter) - Assert.assertFalse(model.showWarningIndicator) - Assert.assertFalse(model.showInfoIndicator) - Assert.assertTrue(model.showSuccessfulTests) - Assert.assertTrue(model.showDisabledTests) - Assert.assertFalse(model.isShowTestDescription) - Assert.assertTrue(model.syncDetailTab) - Assert.assertEquals("test_", model.testPackagePrefix) - Assert.assertEquals("", model.testPackageSuffix) - Assert.assertEquals("", model.testUnitPrefix) - Assert.assertEquals("", model.testUnitSuffix) - Assert.assertEquals(1, model.numberOfTestsPerUnit) - Assert.assertFalse(model.checkGenerateUtplsqlTest) - Assert.assertTrue(model.generateComments) - Assert.assertFalse(model.disableTests) - Assert.assertEquals("alltests", model.suitePath) - Assert.assertEquals(3, model.indentSpaces) - Assert.assertTrue(model.generateFiles) - Assert.assertEquals(PreferenceModel.DEFAULT_OUTPUT_DIRECTORY, model.outputDirectory) - Assert.assertEquals(false, model.deleteExistingFiles) - Assert.assertEquals("utPLSQL", model.rootFolderInOddgenView) - } -} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/preference/PreferencePanelTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/preference/PreferencePanelTest.java new file mode 100644 index 00000000..a27f8498 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/preference/PreferencePanelTest.java @@ -0,0 +1,47 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.preference; + +import java.awt.Dimension; +import java.awt.Toolkit; + +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.model.SystemTools; +import org.utplsql.sqldev.ui.preference.PreferencePanel; + +public class PreferencePanelTest { + + @Test + public void layout() { + final JFrame frame = new JFrame("Preference Panel"); + SwingUtilities.invokeLater(() -> { + final PreferencePanel panel = new PreferencePanel(); + frame.add(panel); + frame.setPreferredSize(new Dimension(600, 400)); + frame.pack(); + final Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); + frame.setLocation(dim.width / 2 - frame.getSize().width / 2, dim.height / 2 - frame.getSize().height / 2); + frame.setVisible(true); + }); + SystemTools.sleep(4 * 1000); + Assert.assertNotNull(frame); + frame.dispose(); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/preference/PreferencePanelTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/preference/PreferencePanelTest.xtend deleted file mode 100644 index 2f5b66f9..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/preference/PreferencePanelTest.xtend +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test.preference - -import java.awt.Dimension -import java.awt.Toolkit -import javax.swing.JFrame -import javax.swing.SwingUtilities -import org.junit.Test -import org.utplsql.sqldev.ui.preference.PreferencePanel - -class PreferencePanelTest { - - @Test - def void layout() { - val frame = new JFrame("Preference Panel") - SwingUtilities.invokeLater(new Runnable() { - override run() { - val panel = new PreferencePanel - frame.add(panel) - frame.preferredSize = new Dimension(600, 400) - frame.pack - val dim = Toolkit.getDefaultToolkit().getScreenSize(); - frame.setLocation(dim.width / 2 - frame.getSize().width / 2, dim.height / 2 - frame.getSize().height / 2); - frame.setVisible(true) - } - }); - Thread.sleep(4 * 1000) - frame.dispose - } - -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/ExpectationTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/ExpectationTest.java new file mode 100644 index 00000000..554e3741 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/ExpectationTest.java @@ -0,0 +1,59 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.runner; + +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.model.runner.Expectation; + +public class ExpectationTest { + + private Expectation getExpectationWithDescription() { + Expectation ex = new Expectation(); + ex.setDescription("This assert must fail"); + ex.setMessage("at: 1 (number) was expected to equal: 2 (number)"); + ex.setCaller("\"SCOTT.JUNIT_UTPLSQL_TEST1_PKG.TEST_2_NOK\", line 14 ut.expect(1, 'This assert must fail').to_equal(2);"); + return ex; + } + + private Expectation getExpectationWithoutDescription() { + Expectation ex = new Expectation(); + ex.setMessage("at: 1 (number) was expected to equal: 2 (number)"); + ex.setCaller("\"SCOTT.JUNIT_UTPLSQL_TEST1_PKG.TEST_3_NOK\", line 42 ut.expect(1).to_equal(2);"); + return ex; + } + + @Test + public void failedExpectationCallerLine() { + final Integer actual = getExpectationWithDescription().getCallerLine(); + final Integer expected = Integer.valueOf(14); + Assert.assertEquals(expected, actual); + } + + @Test + public void shortFailureTextWithDescription() { + final String actual = getExpectationWithDescription().getShortFailureText(); + final String expected = "This assert must fail (line 14)"; + Assert.assertEquals(expected, actual); + } + + @Test + public void shortFailureTextWithoutDescription() { + final String actual = getExpectationWithoutDescription().getShortFailureText(); + final String expected = "Line 42"; + Assert.assertEquals(expected, actual); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/ExpectationTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/ExpectationTest.xtend deleted file mode 100644 index 7c4cff39..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/ExpectationTest.xtend +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test.runner - -import org.junit.Assert -import org.junit.Before -import org.junit.Test -import org.utplsql.sqldev.model.runner.Expectation - -class ExpectationTest { - var Expectation exceptionWithDescription - var Expectation exceptionWithoutDescription - - @Before - def void setup() { - exceptionWithDescription = new Expectation - exceptionWithDescription.description = '''This assert must fail''' - exceptionWithDescription.message = '''at: 1 (number) was expected to equal: 2 (number)''' - exceptionWithDescription.caller = '''"SCOTT.JUNIT_UTPLSQL_TEST1_PKG.TEST_2_NOK", line 14 ut.expect(1, 'This assert must fail').to_equal(2);''' - exceptionWithoutDescription = new Expectation - exceptionWithoutDescription.message = exceptionWithDescription.message - exceptionWithoutDescription.caller = exceptionWithDescription.caller - exceptionWithoutDescription.message = '''at: 1 (number) was expected to equal: 2 (number)''' - exceptionWithoutDescription.caller = '''"SCOTT.JUNIT_UTPLSQL_TEST1_PKG.TEST_3_NOK", line 42 ut.expect(1).to_equal(2);''' - } - - @Test - def void failedExpectationCallerLine() { - val actual = exceptionWithDescription.callerLine - val expected = new Integer(14) - Assert.assertEquals(expected, actual) - } - - @Test - def void shortFailureTextWithDescription() { - val actual = exceptionWithDescription.shortFailureText - val expected = 'This assert must fail (line 14)' - Assert.assertEquals(expected, actual) - } - - @Test - def void shortFailureTextWithoutDescription() { - val actual = exceptionWithoutDescription.shortFailureText - val expected = 'Line 42' - Assert.assertEquals(expected, actual) - } - -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/SmartTimeTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/SmartTimeTest.java new file mode 100644 index 00000000..640e9ad3 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/SmartTimeTest.java @@ -0,0 +1,147 @@ +/* + * Copyright 2019 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.runner; + +import org.junit.Assert; +import org.junit.Test; +import org.utplsql.sqldev.ui.runner.SmartTime; + +public class SmartTimeTest { + + @Test + public void null_default() { + final String effective = new SmartTime(null, false).toString(); + Assert.assertEquals(null, effective); + } + + @Test + public void null_smart() { + final String effective = new SmartTime(null, true).toString(); + Assert.assertEquals(null, effective); + } + + @Test + public void ms_0_default() { + final String effective = new SmartTime(0.0, false).toString(); + Assert.assertEquals("0.000", effective); + } + + @Test + public void ms_0_smart() { + final String effective = new SmartTime(0.0, true).toString(); + Assert.assertEquals("0 ms", effective); + } + + @Test + public void ms_999_default() { + final String effective = new SmartTime(0.999, false).toString(); + Assert.assertEquals("0.999", effective); + } + + @Test + public void ms_999_smart() { + final String effective = new SmartTime(0.999, true).toString(); + Assert.assertEquals("999 ms", effective); + } + + @Test + public void s_1_default() { + final String effective = new SmartTime(1.0, false).toString(); + Assert.assertEquals("1.000", effective); + } + + @Test + public void s_1_smart() { + final String effective = new SmartTime(1.0, true).toString(); + Assert.assertEquals("1.000 s", effective); + } + + @Test + public void s_59_default() { + final String effective = new SmartTime(59.999, false).toString(); + Assert.assertEquals("59.999", effective); + } + + @Test + public void s_59_smart() { + final String effective = new SmartTime(59.999, true).toString(); + Assert.assertEquals("59.999 s", effective); + } + + @Test + public void min_1_default() { + final String effective = new SmartTime(60.0, false).toString(); + Assert.assertEquals("60.000", effective); + } + + @Test + public void min_1_smart() { + final String effective = new SmartTime(60.0, true).toString(); + Assert.assertEquals("1.00 min", effective); + } + + @Test + public void min_59_default() { + final String effective = new SmartTime(3599.999, false).toString(); + Assert.assertEquals("3,599.999", effective); + } + + @Test + public void min_59_smart_and_rounded() { + final String effective = new SmartTime(3599.999, true).toString(); + Assert.assertEquals("60.00 min", effective); + } + + @Test + public void h_1_default() { + final String effective = new SmartTime(3600.0, false).toString(); + Assert.assertEquals("3,600.000", effective); + } + + @Test + public void h_1_smart() { + final String effective = new SmartTime(3600.0, true).toString(); + Assert.assertEquals("1.00 h", effective); + } + + @Test + public void h_max_default() { + final String effective = new SmartTime(99999.999, false).toString(); + Assert.assertEquals("99,999.999", effective); + } + + @Test + public void h_max_smart() { + final String effective = new SmartTime(99999.999, true).toString(); + Assert.assertEquals("27.78 h", effective); + } + + @Test + public void h_higher_than_max_default() { + // larger than format mask + // grouping separator applied, even if not specified in format mask + final String effective = new SmartTime(100000000.0, false).toString(); + Assert.assertEquals("100,000,000.000", effective); + } + + @Test + public void h_higher_than_max_smart() { + // larger than format mask + // no grouping separator applied (that's ok) + final String effective = new SmartTime(100000000.0, true).toString(); + Assert.assertEquals("27777.78 h", effective); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/SmartTimeTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/SmartTimeTest.xtend deleted file mode 100644 index 8b104e27..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/SmartTimeTest.xtend +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2019 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.utplsql.sqldev.test.runner - -import org.junit.Assert -import org.junit.Test -import org.utplsql.sqldev.ui.runner.SmartTime - -class SmartTimeTest { - - @Test - def null_default() { - val effective = (new SmartTime(null, false)).toString - Assert.assertEquals(null, effective); - } - - @Test - def null_smart() { - val effective = (new SmartTime(null, true)).toString - Assert.assertEquals(null, effective); - } - - @Test - def ms_0_default() { - val effective = (new SmartTime(0.0, false)).toString - Assert.assertEquals("0.000", effective); - } - - @Test - def ms_0_smart() { - val effective = (new SmartTime(0.0, true)).toString - Assert.assertEquals("0 ms", effective); - } - - @Test - def ms_999_default() { - val effective = (new SmartTime(0.999, false)).toString - Assert.assertEquals("0.999", effective); - } - - @Test - def ms_999_smart() { - val effective = (new SmartTime(0.999, true)).toString - Assert.assertEquals("999 ms", effective); - } - - @Test - def s_1_default() { - val effective = (new SmartTime(1.0, false)).toString - Assert.assertEquals("1.000", effective); - } - - @Test - def s_1_smart() { - val effective = (new SmartTime(1.0, true)).toString - Assert.assertEquals("1.000 s", effective); - } - - @Test - def s_59_default() { - val effective = (new SmartTime(59.999, false)).toString - Assert.assertEquals("59.999", effective); - } - - @Test - def s_59_smart() { - val effective = (new SmartTime(59.999, true)).toString - Assert.assertEquals("59.999 s", effective); - } - - @Test - def min_1_default() { - val effective = (new SmartTime(60.0, false)).toString - Assert.assertEquals("60.000", effective); - } - - @Test - def min_1_smart() { - val effective = (new SmartTime(60.0, true)).toString - Assert.assertEquals("1.00 min", effective); - } - - @Test - def min_59_default() { - val effective = (new SmartTime(3599.999, false)).toString - Assert.assertEquals("3,599.999", effective); - } - - @Test - def min_59_smart_and_rounded() { - val effective = (new SmartTime(3599.999, true)).toString - Assert.assertEquals("60.00 min", effective); - } - - @Test - def h_1_default() { - val effective = (new SmartTime(3600.0, false)).toString - Assert.assertEquals("3,600.000", effective); - } - - @Test - def h_1_smart() { - val effective = (new SmartTime(3600.0, true)).toString - Assert.assertEquals("1.00 h", effective); - } - - @Test - def h_max_default() { - val effective = (new SmartTime(99999.999, false)).toString - Assert.assertEquals("99,999.999", effective); - } - - @Test - def h_max_smart() { - val effective = (new SmartTime(99999.999, true)).toString - Assert.assertEquals("27.78 h", effective); - } - - @Test - def h_higher_than_max_default() { - // larger than format mask - // grouping separator applied, even if not specified in format mask - val effective = (new SmartTime(100000000.0, false)).toString - Assert.assertEquals("100,000,000.000", effective); - } - - @Test - def h_higher_than_max_smart() { - // larger than format mask - // no grouping separator applied (that's ok) - val effective = (new SmartTime(100000000.0, true)).toString - Assert.assertEquals("27777.78 h", effective); - } -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerPanelTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerPanelTest.java new file mode 100644 index 00000000..59776833 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerPanelTest.java @@ -0,0 +1,104 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.runner; + +import java.awt.Component; +import java.awt.Dimension; +import java.awt.Toolkit; +import java.util.Arrays; +import java.util.UUID; + +import javax.swing.JFrame; +import javax.swing.SwingUtilities; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.utplsql.sqldev.model.SystemTools; +import org.utplsql.sqldev.model.runner.Run; +import org.utplsql.sqldev.resources.UtplsqlResources; +import org.utplsql.sqldev.ui.runner.RunnerPanel; + +public class UtplsqlRunnerPanelTest { + private Run run; + + @Before + public void setup() { + final String reporterId = UUID.randomUUID().toString().replace("-", ""); + run = new Run(null, reporterId, Arrays.asList()); + run.setStartTime("2019-06-09T13:42:42.123456"); + run.getCounter().setDisabled(0); + run.getCounter().setSuccess(0); + run.getCounter().setFailure(0); + run.getCounter().setError(0); + run.getCounter().setWarning(0); + run.setTotalNumberOfTests(5); + run.setCurrentTestNumber(0); + } + + @Test + public void showGUI() { + final long start = System.currentTimeMillis(); + final JFrame frame = new JFrame("utPLSQL Runner Panel"); + frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); + final RunnerPanel panel = new RunnerPanel(); + final Component gui = panel.getGUI(); + panel.setModel(run); + + SwingUtilities.invokeLater(() -> { + frame.add(gui); + frame.pack(); + final Dimension dim = Toolkit.getDefaultToolkit().getScreenSize(); + frame.setSize(600, 600); + frame.setLocation(dim.width / 2 - frame.getSize().width / 2, dim.height / 2 - frame.getSize().height / 2); + frame.setVisible(true); + }); + + run.setStatus("starting"); + panel.update(run.getReporterId()); + SystemTools.sleep(3000); + + run.getCounter().setSuccess(run.getCounter().getSuccess() + 1); + run.setStatus("utplsql.test.a"); + panel.update(run.getReporterId()); + SystemTools.sleep(500); + + run.getCounter().setSuccess(run.getCounter().getSuccess() + 1); + run.setStatus("utplsql.test.b"); + panel.update(run.getReporterId()); + SystemTools.sleep(500); + + run.getCounter().setSuccess(run.getCounter().getSuccess() + 1); + run.setStatus("utplsql.test.c"); + panel.update(run.getReporterId()); + SystemTools.sleep(500); + + run.getCounter().setFailure(run.getCounter().getFailure() + 1); + run.setStatus("utplsql.test.d"); + panel.update(run.getReporterId()); + SystemTools.sleep(500); + + run.getCounter().setSuccess(run.getCounter().getSuccess() + 1); + run.setStatus("utplsql.test.e"); + final long end = System.currentTimeMillis(); + run.setExecutionTime(Double.valueOf(end - start) / 1000); + run.setStatus(UtplsqlResources.getString("RUNNER_FINNISHED_TEXT")); + panel.update(run.getReporterId()); + SystemTools.sleep(2000); + Assert.assertNotNull(frame); + frame.dispose(); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerPanelTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerPanelTest.xtend deleted file mode 100644 index c83d8950..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerPanelTest.xtend +++ /dev/null @@ -1,98 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.utplsql.sqldev.test.runner - -import java.awt.Toolkit -import java.util.UUID -import javax.swing.JFrame -import javax.swing.SwingUtilities -import org.junit.Before -import org.junit.Test -import org.utplsql.sqldev.model.runner.Run -import org.utplsql.sqldev.resources.UtplsqlResources -import org.utplsql.sqldev.ui.runner.RunnerPanel - -class UtplsqlRunnerPanelTest { - var Run run - - @Before - def void setup() { - val reporterId = UUID.randomUUID().toString.replace("-", "") - run = new Run(null, reporterId, #[]) - run.startTime = "2019-06-09T13:42:42.123456" - run.counter.disabled = 0 - run.counter.success = 0 - run.counter.failure = 0 - run.counter.error = 0 - run.counter.warning = 0 - run.totalNumberOfTests = 5 - run.currentTestNumber = 0 - } - - @Test - def void showGUI() { - val start = System.currentTimeMillis - val frame = new JFrame("utPLSQL Runner Panel") - frame.defaultCloseOperation = JFrame.EXIT_ON_CLOSE; - val panel = new RunnerPanel - val gui = panel.getGUI - panel.model = run - - SwingUtilities.invokeLater(new Runnable() { - override run() { - frame.add(gui) - frame.pack - val dim = Toolkit.getDefaultToolkit().getScreenSize(); - frame.setLocation(dim.width / 2 - frame.getSize().width / 2, dim.height / 2 - frame.getSize().height / 2); - frame.setVisible(true) - } - }); - - run.status="starting" - panel.update(run.reporterId) - Thread.sleep(3000); - - run.counter.success = run.counter.success + 1 - run.status="utplsql.test.a" - panel.update(run.reporterId) - Thread.sleep(500); - - run.counter.success = run.counter.success + 1 - run.status="utplsql.test.b" - panel.update(run.reporterId) - Thread.sleep(500); - - run.counter.success = run.counter.success + 1 - run.status="utplsql.test.c" - panel.update(run.reporterId) - Thread.sleep(500); - - run.counter.failure = run.counter.failure + 1 - run.status="utplsql.test.d" - panel.update(run.reporterId) - Thread.sleep(500); - - run.counter.success = run.counter.success + 1 - run.status="utplsql.test.e" - val end = System.currentTimeMillis - run.executionTime = new Double(end-start)/1000 - run.status = UtplsqlResources.getString("RUNNER_FINNISHED_TEXT") - panel.update(run.reporterId) - Thread.sleep(2000); - frame.dispose - } - -} \ No newline at end of file diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerTest.java b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerTest.java new file mode 100644 index 00000000..c3531381 --- /dev/null +++ b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerTest.java @@ -0,0 +1,143 @@ +/* + * Copyright 2018 Philipp Salvisberg + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.utplsql.sqldev.test.runner; + +import java.sql.Connection; +import java.util.Arrays; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.springframework.jdbc.datasource.SingleConnectionDataSource; +import org.utplsql.sqldev.model.DatabaseTools; +import org.utplsql.sqldev.model.SystemTools; +import org.utplsql.sqldev.runner.UtplsqlRunner; +import org.utplsql.sqldev.test.AbstractJdbcTest; + +public class UtplsqlRunnerTest extends AbstractJdbcTest { + + @Before + public void setup() { + StringBuilder sb = new StringBuilder(); + sb.append("CREATE OR REPLACE PACKAGE junit_utplsql_test1_pkg is\n"); + sb.append(" --%suite(JUnit testing)\n"); + sb.append(" --%suitepath(a)\n"); + sb.append(" /* tags annotation without parameter will raise a warning */\n"); + sb.append(" --%tags\n\n"); + + sb.append(" --%context(test context)\n\n"); + + sb.append(" --%test(test 1 - OK)\n"); + sb.append(" PROCEDURE test_1_ok;\n\n"); + + sb.append(" --%test(test 2 - NOK)\n"); + sb.append(" PROCEDURE test_2_nok;\n\n"); + + sb.append(" --%test(test 3 - disabled)\n"); + sb.append(" --%disabled\n"); + sb.append(" PROCEDURE test_3_disabled;\n\n"); + + sb.append(" --%test(test 4 - errored)\n"); + sb.append(" PROCEDURE test_4_errored;\n\n"); + + sb.append(" --%test(test 5 - warnings)\n"); + sb.append(" PROCEDURE test_5_warnings;\n\n"); + + sb.append(" --%endcontext\n\n"); + + sb.append(" --%afterall\n"); + sb.append(" procedure print_and_raise;\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + + sb.setLength(0); + sb.append("CREATE OR REPLACE PACKAGE BODY junit_utplsql_test1_pkg IS\n"); + sb.append(" PROCEDURE test_1_ok IS\n"); + sb.append(" BEGIN\n"); + sb.append(" dbms_output.put_line('start test 1');\n"); + sb.append(" dbms_session.sleep(1);\n"); + sb.append(" ut.expect(1).to_equal(1);\n"); + sb.append(" dbms_output.put_line('end test 1');\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_2_nok IS\n"); + sb.append(" BEGIN\n"); + sb.append(" dbms_output.put_line('start test 2');\n"); + sb.append(" dbms_session.sleep(2);\n"); + sb.append(" ut.expect(1, 'first assert.').to_equal(2);\n"); + sb.append(" ut.expect(1, 'second assert.').to_equal(2);\n"); + sb.append(" dbms_output.put_line('end test 2');\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_3_disabled IS\n"); + sb.append(" BEGIN\n"); + sb.append(" NULL;\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_4_errored IS\n"); + sb.append(" BEGIN\n"); + sb.append(" EXECUTE IMMEDIATE 'bla bla';\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE test_5_warnings IS\n"); + sb.append(" BEGIN\n"); + sb.append(" COMMIT; -- will raise a warning\n"); + sb.append(" ut.expect(1).to_equal(1);\n"); + sb.append(" END;\n\n"); + + sb.append(" PROCEDURE print_and_raise IS\n"); + sb.append(" BEGIN\n"); + sb.append(" dbms_output.put_line('Now, a no_data_found exception is raised');\n"); + sb.append(" dbms_output.put_line('dbms_output and error stack is reported for this suite.');\n"); + sb.append(" dbms_output.put_line('A runtime error in afterall is counted as a warning.');\n"); + sb.append(" RAISE no_data_found;\n"); + sb.append(" END;\n"); + sb.append("END;"); + jdbcTemplate.execute(sb.toString()); + } + + @After + public void teardown() { + executeAndIgnore(jdbcTemplate, "DROP PACKAGE junit_utplsql_test1_pkg"); + } + + @Test + public void runTestsWithMaxTime() { + final SingleConnectionDataSource ds1 = new SingleConnectionDataSource(); + ds1.setDriverClassName("oracle.jdbc.OracleDriver"); + ds1.setUrl(dataSource.getUrl()); + ds1.setUsername(dataSource.getUsername()); + ds1.setPassword(dataSource.getPassword()); + final Connection producerConn = DatabaseTools.getConnection(ds1); + + final SingleConnectionDataSource ds2 = new SingleConnectionDataSource(); + ds2.setDriverClassName("oracle.jdbc.OracleDriver"); + ds2.setUrl(dataSource.getUrl()); + ds2.setUsername(dataSource.getUsername()); + ds2.setPassword(dataSource.getPassword()); + final Connection consumerConn = DatabaseTools.getConnection(ds2); + + UtplsqlRunner runner = new UtplsqlRunner(Arrays.asList(":a"), producerConn, consumerConn); + runner.runTestAsync(); + + SystemTools.waitForThread(runner.getProducerThread(), 200000); + SystemTools.waitForThread(runner.getConsumerThread(), 200000); + SystemTools.sleep(4 * 1000); + Assert.assertNotNull(runner); + runner.dispose(); + } +} diff --git a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerTest.xtend b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerTest.xtend deleted file mode 100644 index 23b840b5..00000000 --- a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerTest.xtend +++ /dev/null @@ -1,136 +0,0 @@ -/* - * Copyright 2018 Philipp Salvisberg - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - package org.utplsql.sqldev.test.runner - -import org.junit.AfterClass -import org.junit.BeforeClass -import org.junit.Test -import org.springframework.jdbc.BadSqlGrammarException -import org.springframework.jdbc.datasource.SingleConnectionDataSource -import org.utplsql.sqldev.runner.UtplsqlRunner -import org.utplsql.sqldev.test.AbstractJdbcTest - -class UtplsqlRunnerTest extends AbstractJdbcTest { - - @BeforeClass - def static void setup() { - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE junit_utplsql_test1_pkg is - --%suite(JUnit testing) - --%suitepath(a) - /* tags annotation without parameter will raise a warning */ - --%tags - - --%context(test context) - - --%test(test 1 - OK) - PROCEDURE test_1_ok; - - --%test(test 2 - NOK) - PROCEDURE test_2_nok; - - --%test(test 3 - disabled) - --%disabled - PROCEDURE test_3_disabled; - - --%test(test 4 - errored) - PROCEDURE test_4_errored; - - --%test(test 5 - warnings) - PROCEDURE test_5_warnings; - - --%endcontext - - --%afterall - procedure print_and_raise; - END; - ''') - jdbcTemplate.execute(''' - CREATE OR REPLACE PACKAGE BODY junit_utplsql_test1_pkg IS - PROCEDURE test_1_ok IS - BEGIN - dbms_output.put_line('start test 1'); - dbms_session.sleep(1); - ut.expect(1).to_equal(1); - dbms_output.put_line('end test 1'); - END; - - PROCEDURE test_2_nok IS - BEGIN - dbms_output.put_line('start test 2'); - dbms_session.sleep(2); - ut.expect(1, 'first assert.').to_equal(2); - ut.expect(1, 'second assert.').to_equal(2); - dbms_output.put_line('end test 2'); - END; - - PROCEDURE test_3_disabled IS - BEGIN - NULL; - END; - - PROCEDURE test_4_errored IS - BEGIN - EXECUTE IMMEDIATE 'bla bla'; - END; - - PROCEDURE test_5_warnings IS - BEGIN - COMMIT; -- will raise a warning - ut.expect(1).to_equal(1); - END; - - PROCEDURE print_and_raise IS - BEGIN - dbms_output.put_line('Now, a no_data_found exception is raised'); - dbms_output.put_line('dbms_output and error stack is reported for this suite.'); - dbms_output.put_line('A runtime error in afterall is counted as a warning.'); - RAISE no_data_found; - END; - END; - ''') - } - - @AfterClass - def static void teardown() { - try { - jdbcTemplate.execute("DROP PACKAGE junit_utplsql_test1_pkg") - } catch (BadSqlGrammarException e) { - // ignore - } - } - - @Test - def void runTestsWithMaxTime() { - var ds1 = new SingleConnectionDataSource() - ds1.driverClassName = "oracle.jdbc.OracleDriver" - ds1.url = dataSource.url - ds1.username = dataSource.username - ds1.password = dataSource.password - var ds2 = new SingleConnectionDataSource() - ds2.driverClassName = "oracle.jdbc.OracleDriver" - ds2.url = dataSource.url - ds2.username = dataSource.username - ds2.password = dataSource.password - var runner = new UtplsqlRunner(#[":a"], ds1.connection, ds2.connection) - runner.runTestAsync - runner.producerThread.join(200000) - runner.consumerThread.join(200000) - Thread.sleep(4 * 1000) - runner.dispose - } - -} \ No newline at end of file