Skip to content

Commit 304c8bc

Browse files
Merge pull request #105 from utPLSQL/feature/issue-102-code-coverage
#102 Run tests with code coverage
2 parents 584cc33 + 64cbad0 commit 304c8bc

File tree

11 files changed

+394
-69
lines changed

11 files changed

+394
-69
lines changed

sqldev/src/main/java/org/utplsql/sqldev/coverage/CodeCoverageReporter.java

Lines changed: 92 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,19 +22,28 @@
2222
import java.sql.Connection;
2323
import java.util.ArrayList;
2424
import java.util.Arrays;
25+
import java.util.HashMap;
2526
import java.util.List;
27+
import java.util.Map;
2628
import java.util.logging.Logger;
29+
import java.util.stream.Collectors;
2730

31+
import org.utplsql.sqldev.dal.RealtimeReporterDao;
2832
import org.utplsql.sqldev.dal.UtplsqlDao;
2933
import org.utplsql.sqldev.exception.GenericDatabaseAccessException;
3034
import org.utplsql.sqldev.exception.GenericRuntimeException;
3135
import org.utplsql.sqldev.model.DatabaseTools;
3236
import org.utplsql.sqldev.model.FileTools;
37+
import org.utplsql.sqldev.model.preference.PreferenceModel;
38+
import org.utplsql.sqldev.runner.UtplsqlRunner;
3339
import org.utplsql.sqldev.ui.coverage.CodeCoverageReporterDialog;
3440

41+
import oracle.ide.config.Preferences;
42+
3543
public class CodeCoverageReporter {
3644
private static final Logger logger = Logger.getLogger(CodeCoverageReporter.class.getName());
3745

46+
private String connectionName;
3847
private Connection conn;
3948
private List<String> pathList;
4049
private List<String> includeObjectList;
@@ -47,14 +56,17 @@ public CodeCoverageReporter(final List<String> pathList, final List<String> incl
4756
final String connectionName) {
4857
this.pathList = pathList;
4958
this.includeObjectList = includeObjectList;
59+
setDefaultSchema();
5060
setConnection(connectionName);
5161
}
5262

63+
// constructor for testing purposes only
5364
public CodeCoverageReporter(final List<String> pathList, final List<String> includeObjectList,
5465
final Connection conn) {
5566
this.pathList = pathList;
5667
this.includeObjectList = includeObjectList;
5768
this.conn = conn;
69+
setDefaultSchema();
5870
}
5971

6072
private void setConnection(final String connectionName) {
@@ -64,7 +76,32 @@ private void setConnection(final String connectionName) {
6476
throw new NullPointerException();
6577
} else {
6678
// must be closed manually
67-
conn = DatabaseTools.cloneConnection(connectionName);
79+
this.connectionName = connectionName;
80+
this.conn = DatabaseTools.getConnection(connectionName);
81+
}
82+
}
83+
84+
private void setDefaultSchema() {
85+
if (includeObjectList != null && !includeObjectList.isEmpty()) {
86+
// use the owner with the most hits in includeObjectList
87+
HashMap<String, Integer> owners = new HashMap<>();
88+
for (String entry : includeObjectList) {
89+
String[] obj = entry.toUpperCase().split("\\.");
90+
if (obj.length == 2) {
91+
// only if objectOwner and objectName are available
92+
Integer count = owners.get(obj[0]);
93+
if (count == null) {
94+
count = 1;
95+
} else {
96+
count++;
97+
}
98+
owners.put(obj[0], count);
99+
}
100+
}
101+
List<String> sortedOwners = owners.entrySet().stream()
102+
.sorted(Map.Entry.<String, Integer>comparingByValue().reversed()).map(Map.Entry::getKey)
103+
.collect(Collectors.toList());
104+
schemas = String.join(", ", sortedOwners);
68105
}
69106
}
70107

@@ -83,12 +120,57 @@ private ArrayList<String> toStringList(final String s) {
83120
private void run() {
84121
logger.fine(() -> "Running code coverage reporter for " + pathList + "...");
85122
try {
86-
final UtplsqlDao dal = new UtplsqlDao(conn);
87-
final String content = dal.htmlCodeCoverage(pathList, toStringList(schemas),
123+
final RealtimeReporterDao dao = new RealtimeReporterDao(conn);
124+
PreferenceModel preferences;
125+
try {
126+
preferences = PreferenceModel.getInstance(Preferences.getPreferences());
127+
} catch (NoClassDefFoundError error) {
128+
// not running in SQL Developer (in tests)
129+
preferences = PreferenceModel.getInstance(null);
130+
}
131+
if (preferences.isUseRealtimeReporter() && dao.isSupported() && connectionName != null) {
132+
runCodeCoverageWithRealtimeReporter();
133+
} else {
134+
runCodeCoverageStandalone();
135+
}
136+
} finally {
137+
if (frame != null) {
138+
frame.exit();
139+
}
140+
}
141+
}
142+
143+
private void runCodeCoverageWithRealtimeReporter() {
144+
final UtplsqlRunner runner = new UtplsqlRunner(pathList, toStringList(schemas), toStringList(includeObjects),
145+
toStringList(excludeObjects), connectionName);
146+
runner.runTestAsync();
147+
}
148+
149+
private void runCodeCoverageStandalone() {
150+
Connection coverageConn = null;
151+
try {
152+
coverageConn = conn != null ? conn : DatabaseTools.cloneConnection(connectionName);
153+
final UtplsqlDao dao = new UtplsqlDao(coverageConn);
154+
final String html = dao.htmlCodeCoverage(pathList, toStringList(schemas),
88155
toStringList(includeObjects), toStringList(excludeObjects));
156+
openInBrowser(html);
157+
} finally {
158+
try {
159+
if (coverageConn != null && conn == null) {
160+
// close only if connection has been cloned
161+
DatabaseTools.closeConnection(coverageConn);
162+
}
163+
} catch (GenericDatabaseAccessException e) {
164+
// ignore
165+
}
166+
}
167+
}
168+
169+
public static void openInBrowser(String html) {
170+
try {
89171
final File file = File.createTempFile("utplsql_", ".html");
90172
logger.fine(() -> "Writing result to " + file + "...");
91-
FileTools.writeFile(file.toPath(), Arrays.asList(content.split(System.lineSeparator())), StandardCharsets.UTF_8);
173+
FileTools.writeFile(file.toPath(), Arrays.asList(html.split(System.lineSeparator())), StandardCharsets.UTF_8);
92174
final URL url = file.toURI().toURL();
93175
logger.fine(() -> "Opening " + url.toExternalForm() + " in browser...");
94176
final Desktop desktop = Desktop.isDesktopSupported() ? Desktop.getDesktop() : null;
@@ -97,21 +179,12 @@ private void run() {
97179
logger.fine(() -> url.toExternalForm() + " opened in browser.");
98180
} else {
99181
logger.severe(
100-
() -> "Could not launch " + file + "in browser. No default browser defined on this system.");
182+
() -> "Could not launch " + file + " in browser. No default browser defined on this system.");
101183
}
102184
} catch (Exception e) {
103-
final String msg = "Error while running code coverage for " + pathList + ".";
185+
final String msg = "Error while opening code coverage HTML report in browser.";
104186
logger.severe(() -> msg);
105187
throw new GenericRuntimeException(msg, e);
106-
} finally {
107-
try {
108-
DatabaseTools.closeConnection(conn);
109-
} catch (GenericDatabaseAccessException e) {
110-
// ignore
111-
}
112-
if (frame != null) {
113-
frame.exit();
114-
}
115188
}
116189
}
117190

@@ -143,6 +216,10 @@ public void setSchemas(final String schemas) {
143216
this.schemas = schemas;
144217
}
145218

219+
public String getSchemas() {
220+
return schemas;
221+
}
222+
146223
public void setIncludeObjects(final String includeObjects) {
147224
this.includeObjects = includeObjects;
148225
}

sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterDao.java

Lines changed: 89 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public class RealtimeReporterDao {
6464
public RealtimeReporterDao(final Connection conn) {
6565
this.conn = conn;
6666
jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(conn, true));
67-
jdbcTemplate.setFetchSize(1);
67+
jdbcTemplate.setFetchSize(UtplsqlDao.FETCH_ROWS);
6868
}
6969

7070
public boolean isSupported() {
@@ -93,6 +93,47 @@ public void produceReport(final String reporterId, final List<String> pathList)
9393
jdbcTemplate.update(plsql, binds);
9494
}
9595

96+
public void produceReportWithCoverage(final String realtimeReporterId, final String coverageReporterId,
97+
final List<String> pathList, final List<String> schemaList, final List<String> includeObjectList,
98+
final List<String> excludeObjectList) {
99+
StringBuilder sb = new StringBuilder();
100+
sb.append("DECLARE\n");
101+
sb.append(" l_rt_rep ut_realtime_reporter := ut_realtime_reporter();\n");
102+
sb.append(" l_cov_rep ut_coverage_html_reporter := ut_coverage_html_reporter();\n");
103+
sb.append("BEGIN\n");
104+
sb.append(" l_rt_rep.set_reporter_id(?);\n");
105+
sb.append(" l_rt_rep.output_buffer.init();\n");
106+
sb.append(" l_cov_rep.set_reporter_id(?);\n");
107+
sb.append(" l_cov_rep.output_buffer.init();\n");
108+
sb.append(" sys.dbms_output.enable(NULL);\n");
109+
sb.append(" ut_runner.run(\n");
110+
sb.append(" a_paths => ut_varchar2_list(\n");
111+
sb.append(StringTools.getCSV(pathList, 31));
112+
sb.append(" ),\n");
113+
if (schemaList != null && !schemaList.isEmpty()) {
114+
sb.append(" a_coverage_schemes => ut_varchar2_list(\n");
115+
sb.append(StringTools.getCSV(schemaList, 31));
116+
sb.append(" ),\n");
117+
}
118+
if (includeObjectList != null && !includeObjectList.isEmpty()) {
119+
sb.append(" a_include_objects => ut_varchar2_list(\n");
120+
sb.append(StringTools.getCSV(includeObjectList, 31));
121+
sb.append(" ),\n");
122+
}
123+
if (excludeObjectList != null && !excludeObjectList.isEmpty()) {
124+
sb.append(" a_exclude_objects => ut_varchar2_list(\n");
125+
sb.append(StringTools.getCSV(excludeObjectList, 31));
126+
sb.append(" ),\n");
127+
}
128+
sb.append(" a_reporters => ut_reporters(l_rt_rep, l_cov_rep)\n");
129+
sb.append(" );\n");
130+
sb.append(" sys.dbms_output.disable;\n");
131+
sb.append("END;");
132+
final String plsql = sb.toString();
133+
final Object[] binds = { realtimeReporterId, coverageReporterId };
134+
jdbcTemplate.update(plsql, binds);
135+
}
136+
96137
public void consumeReport(final String reporterId, final RealtimeReporterEventConsumer consumer) {
97138
StringBuilder sb = new StringBuilder();
98139
sb.append("DECLARE\n");
@@ -102,28 +143,63 @@ public void consumeReport(final String reporterId, final RealtimeReporterEventCo
102143
sb.append(" ? := l_reporter.get_lines_cursor();\n");
103144
sb.append("END;");
104145
final String plsql = sb.toString();
105-
jdbcTemplate.execute(plsql, new CallableStatementCallback<Void>() {
146+
jdbcTemplate.setFetchSize(1);
147+
try {
148+
jdbcTemplate.execute(plsql, new CallableStatementCallback<Void>() {
149+
@Override
150+
public Void doInCallableStatement(final CallableStatement cs) throws SQLException {
151+
cs.setString(1, reporterId);
152+
cs.registerOutParameter(2, OracleTypes.CURSOR);
153+
cs.execute();
154+
final ResultSet rs = (ResultSet) cs.getObject(2);
155+
while (rs.next()) {
156+
final String itemType = rs.getString("item_type");
157+
final Clob textClob = rs.getClob("text");
158+
final String textString = textClob.getSubString(1, ((int) textClob.length()));
159+
final RealtimeReporterEvent event = convert(itemType, textString);
160+
if (event != null) {
161+
consumer.process(event);
162+
}
163+
}
164+
rs.close();
165+
return null;
166+
}
167+
});
168+
} finally {
169+
jdbcTemplate.setFetchSize(UtplsqlDao.FETCH_ROWS);
170+
}
171+
}
172+
173+
public String getHtmlCoverage(final String reporterId) {
174+
StringBuilder sb = new StringBuilder();
175+
sb.append("DECLARE\n");
176+
sb.append(" l_reporter ut_coverage_html_reporter := ut_coverage_html_reporter();\n");
177+
sb.append("BEGIN\n");
178+
sb.append(" l_reporter.set_reporter_id(?);\n");
179+
sb.append(" ? := l_reporter.get_lines_cursor();\n");
180+
sb.append("END;");
181+
final String plsql = sb.toString();
182+
return jdbcTemplate.execute(plsql, new CallableStatementCallback<String>() {
106183
@Override
107-
public Void doInCallableStatement(final CallableStatement cs) throws SQLException {
184+
public String doInCallableStatement(final CallableStatement cs) throws SQLException {
108185
cs.setString(1, reporterId);
109186
cs.registerOutParameter(2, OracleTypes.CURSOR);
110187
cs.execute();
111-
final ResultSet rs = ((ResultSet) cs.getObject(2));
188+
final StringBuilder sb = new StringBuilder();
189+
final ResultSet rs = (ResultSet) cs.getObject(2);
112190
while (rs.next()) {
113-
final String itemType = rs.getString("item_type");
114-
final Clob textClob = rs.getClob("text");
115-
final String textString = textClob.getSubString(1, ((int) textClob.length()));
116-
final RealtimeReporterEvent event = convert(itemType, textString);
117-
if (event != null) {
118-
consumer.process(event);
191+
final String text = rs.getString("text");
192+
if (text != null) {
193+
sb.append(text);
194+
sb.append('\n');
119195
}
120196
}
121197
rs.close();
122-
return null;
198+
return sb.toString();
123199
}
124200
});
125-
}
126-
201+
}
202+
127203
private RealtimeReporterEvent convert(final String itemType, final String text) {
128204
logger.fine(() -> "\n---- " + itemType + " ----\n" + text);
129205
try {

sqldev/src/main/java/org/utplsql/sqldev/dal/UtplsqlDao.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ public class UtplsqlDao {
4242
public static final int FIRST_VERSION_WITH_ANNOTATION_API = 3001003;
4343
public static final int FIRST_VERSION_WITHOUT_INTERNAL_API = 3001008;
4444
public static final int FIRST_VERSION_WITH_HAS_SUITES_API = 3001008;
45+
public static final int FETCH_ROWS = 100;
4546
private JdbcTemplate jdbcTemplate;
4647
// cache fields
4748
private Boolean cachedDbaViewAccessible;
@@ -50,6 +51,7 @@ public class UtplsqlDao {
5051

5152
public UtplsqlDao(final Connection conn) {
5253
jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(conn, true));
54+
jdbcTemplate.setFetchSize(FETCH_ROWS);
5355
}
5456

5557
/**
@@ -918,17 +920,17 @@ public String htmlCodeCoverage(final List<String> pathList, final List<String> s
918920
sb.append(" a_paths => ut_varchar2_list(\n");
919921
sb.append(StringTools.getCSV(pathList, 16));
920922
sb.append(" ),\n");
921-
if (!schemaList.isEmpty()) {
923+
if (schemaList != null && !schemaList.isEmpty()) {
922924
sb.append(" a_coverage_schemes => ut_varchar2_list(\n");
923925
sb.append(StringTools.getCSV(schemaList, 16));
924926
sb.append(" ),\n");
925927
}
926-
if (!includeObjectList.isEmpty()) {
928+
if (includeObjectList != null && !includeObjectList.isEmpty()) {
927929
sb.append(" a_include_objects => ut_varchar2_list(\n");
928930
sb.append(StringTools.getCSV(includeObjectList, 16));
929931
sb.append(" ),\n");
930932
}
931-
if (!excludeObjectList.isEmpty()) {
933+
if (excludeObjectList != null && excludeObjectList.isEmpty()) {
932934
sb.append(" a_exclude_objects => ut_varchar2_list(\n");
933935
sb.append(StringTools.getCSV(excludeObjectList, 16));
934936
sb.append(" ),\n");

0 commit comments

Comments
 (0)