diff --git a/sqldev/src/main/java/org/utplsql/sqldev/coverage/CodeCoverageReporter.java b/sqldev/src/main/java/org/utplsql/sqldev/coverage/CodeCoverageReporter.java
index c00a0473..8c86ea7a 100644
--- a/sqldev/src/main/java/org/utplsql/sqldev/coverage/CodeCoverageReporter.java
+++ b/sqldev/src/main/java/org/utplsql/sqldev/coverage/CodeCoverageReporter.java
@@ -22,19 +22,28 @@
 import java.sql.Connection;
 import java.util.ArrayList;
 import java.util.Arrays;
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.logging.Logger;
+import java.util.stream.Collectors;
 
+import org.utplsql.sqldev.dal.RealtimeReporterDao;
 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.model.preference.PreferenceModel;
+import org.utplsql.sqldev.runner.UtplsqlRunner;
 import org.utplsql.sqldev.ui.coverage.CodeCoverageReporterDialog;
 
+import oracle.ide.config.Preferences;
+
 public class CodeCoverageReporter {
     private static final Logger logger = Logger.getLogger(CodeCoverageReporter.class.getName());
 
+    private String connectionName;
     private Connection conn;
     private List<String> pathList;
     private List<String> includeObjectList;
@@ -47,14 +56,17 @@ public CodeCoverageReporter(final List<String> pathList, final List<String> incl
             final String connectionName) {
         this.pathList = pathList;
         this.includeObjectList = includeObjectList;
+        setDefaultSchema();
         setConnection(connectionName);
     }
 
+    // constructor for testing purposes only
     public CodeCoverageReporter(final List<String> pathList, final List<String> includeObjectList,
             final Connection conn) {
         this.pathList = pathList;
         this.includeObjectList = includeObjectList;
         this.conn = conn;
+        setDefaultSchema();
     }
 
     private void setConnection(final String connectionName) {
@@ -64,7 +76,32 @@ private void setConnection(final String connectionName) {
             throw new NullPointerException();
         } else {
             // must be closed manually
-            conn = DatabaseTools.cloneConnection(connectionName);
+            this.connectionName = connectionName;
+            this.conn = DatabaseTools.getConnection(connectionName);
+        }
+    }
+    
+    private void setDefaultSchema() {
+        if (includeObjectList != null && !includeObjectList.isEmpty()) {
+            // use the owner with the most hits in includeObjectList
+            HashMap<String, Integer> owners = new HashMap<>();
+            for (String entry : includeObjectList) {
+                String[] obj = entry.toUpperCase().split("\\.");
+                if (obj.length == 2) {
+                    // only if objectOwner and objectName are available
+                    Integer count = owners.get(obj[0]);
+                    if (count == null) {
+                        count = 1;
+                    } else {
+                        count++;
+                    }
+                    owners.put(obj[0], count);
+                }
+            }
+            List<String> sortedOwners = owners.entrySet().stream()
+                    .sorted(Map.Entry.<String, Integer>comparingByValue().reversed()).map(Map.Entry::getKey)
+                    .collect(Collectors.toList());
+            schemas = String.join(", ", sortedOwners);
         }
     }
 
@@ -83,12 +120,57 @@ private ArrayList<String> toStringList(final String s) {
     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),
+            final RealtimeReporterDao dao = new RealtimeReporterDao(conn);
+            PreferenceModel preferences;
+            try {
+                preferences = PreferenceModel.getInstance(Preferences.getPreferences());
+            } catch (NoClassDefFoundError error) {
+                // not running in SQL Developer (in tests)
+                preferences = PreferenceModel.getInstance(null);
+            }
+            if (preferences.isUseRealtimeReporter() && dao.isSupported() && connectionName != null) {
+                runCodeCoverageWithRealtimeReporter();
+            } else {
+                runCodeCoverageStandalone();
+            }
+        } finally {
+            if (frame != null) {
+                frame.exit();
+            }
+        }
+    }
+    
+    private void runCodeCoverageWithRealtimeReporter() {
+        final UtplsqlRunner runner = new UtplsqlRunner(pathList, toStringList(schemas), toStringList(includeObjects),
+                toStringList(excludeObjects), connectionName);
+        runner.runTestAsync();
+    }
+    
+    private void runCodeCoverageStandalone() {
+        Connection coverageConn = null;
+        try {
+            coverageConn = conn != null ? conn : DatabaseTools.cloneConnection(connectionName);
+            final UtplsqlDao dao = new UtplsqlDao(coverageConn);
+            final String html = dao.htmlCodeCoverage(pathList, toStringList(schemas),
                     toStringList(includeObjects), toStringList(excludeObjects));
+            openInBrowser(html);
+        } finally {
+            try {
+                if (coverageConn != null && conn == null) {
+                    // close only if connection has been cloned
+                    DatabaseTools.closeConnection(coverageConn);
+                }
+            } catch (GenericDatabaseAccessException e) {
+                // ignore
+            }
+        }
+    }
+    
+    public static void openInBrowser(String html) {
+        try {
             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);
+            FileTools.writeFile(file.toPath(), Arrays.asList(html.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;
@@ -97,21 +179,12 @@ private void run() {
                 logger.fine(() -> url.toExternalForm() + " opened in browser.");
             } else {
                 logger.severe(
-                        () -> "Could not launch " + file + "in browser. No default browser defined on this system.");
+                        () -> "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 + ".";
+            final String msg = "Error while opening code coverage HTML report in browser.";
             logger.severe(() -> msg);
             throw new GenericRuntimeException(msg, e);
-        } finally {
-            try {
-                DatabaseTools.closeConnection(conn);
-            } catch (GenericDatabaseAccessException e) {
-                // ignore
-            }
-            if (frame != null) {
-                frame.exit();
-            }
         }
     }
 
@@ -143,6 +216,10 @@ public void setSchemas(final String schemas) {
         this.schemas = schemas;
     }
 
+    public String getSchemas() {
+        return schemas;
+    }
+
     public void setIncludeObjects(final String includeObjects) {
         this.includeObjects = includeObjects;
     }
diff --git a/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterDao.java b/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterDao.java
index 87be783c..042d6810 100644
--- a/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterDao.java
+++ b/sqldev/src/main/java/org/utplsql/sqldev/dal/RealtimeReporterDao.java
@@ -64,7 +64,7 @@ public class RealtimeReporterDao {
     public RealtimeReporterDao(final Connection conn) {
         this.conn = conn;
         jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(conn, true));
-        jdbcTemplate.setFetchSize(1);
+        jdbcTemplate.setFetchSize(UtplsqlDao.FETCH_ROWS);
     }
 
     public boolean isSupported() {
@@ -93,6 +93,47 @@ public void produceReport(final String reporterId, final List<String> pathList)
         jdbcTemplate.update(plsql, binds);
     }
 
+    public void produceReportWithCoverage(final String realtimeReporterId, final String coverageReporterId,
+            final List<String> pathList, final List<String> schemaList, final List<String> includeObjectList,
+            final List<String> excludeObjectList) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("DECLARE\n");
+        sb.append("   l_rt_rep  ut_realtime_reporter      := ut_realtime_reporter();\n");
+        sb.append("   l_cov_rep ut_coverage_html_reporter := ut_coverage_html_reporter();\n");
+        sb.append("BEGIN\n");
+        sb.append("   l_rt_rep.set_reporter_id(?);\n");
+        sb.append("   l_rt_rep.output_buffer.init();\n");
+        sb.append("   l_cov_rep.set_reporter_id(?);\n");
+        sb.append("   l_cov_rep.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, 31));
+        sb.append("                            ),\n");
+        if (schemaList != null && !schemaList.isEmpty()) {
+            sb.append("      a_coverage_schemes => ut_varchar2_list(\n");
+            sb.append(StringTools.getCSV(schemaList, 31));
+            sb.append("                            ),\n");
+        }
+        if (includeObjectList != null && !includeObjectList.isEmpty()) {
+            sb.append("     a_include_objects   => ut_varchar2_list(\n");
+            sb.append(StringTools.getCSV(includeObjectList, 31));
+            sb.append("                            ),\n");
+        }
+        if (excludeObjectList != null && !excludeObjectList.isEmpty()) {
+            sb.append("     a_exclude_objects   => ut_varchar2_list(\n");
+            sb.append(StringTools.getCSV(excludeObjectList, 31));
+            sb.append("                            ),\n");
+        }
+        sb.append("      a_reporters        => ut_reporters(l_rt_rep, l_cov_rep)\n");
+        sb.append("   );\n");
+        sb.append("   sys.dbms_output.disable;\n");
+        sb.append("END;");
+        final String plsql = sb.toString();
+        final Object[] binds = { realtimeReporterId, coverageReporterId };
+        jdbcTemplate.update(plsql, binds);
+    }
+
     public void consumeReport(final String reporterId, final RealtimeReporterEventConsumer consumer) {
         StringBuilder sb = new StringBuilder();
         sb.append("DECLARE\n");
@@ -102,28 +143,63 @@ public void consumeReport(final String reporterId, final RealtimeReporterEventCo
         sb.append("   ? := l_reporter.get_lines_cursor();\n");
         sb.append("END;");
         final String plsql = sb.toString();
-        jdbcTemplate.execute(plsql, new CallableStatementCallback<Void>() {
+        jdbcTemplate.setFetchSize(1);
+        try {
+            jdbcTemplate.execute(plsql, new CallableStatementCallback<Void>() {
+                @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;
+                }
+            });
+        } finally {
+            jdbcTemplate.setFetchSize(UtplsqlDao.FETCH_ROWS);
+        }
+    }
+
+    public String getHtmlCoverage(final String reporterId) {
+        StringBuilder sb = new StringBuilder();
+        sb.append("DECLARE\n");
+        sb.append("   l_reporter ut_coverage_html_reporter := ut_coverage_html_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();
+        return jdbcTemplate.execute(plsql, new CallableStatementCallback<String>() {
             @Override
-            public Void doInCallableStatement(final CallableStatement cs) throws SQLException {
+            public String doInCallableStatement(final CallableStatement cs) throws SQLException {
                 cs.setString(1, reporterId);
                 cs.registerOutParameter(2, OracleTypes.CURSOR);
                 cs.execute();
-                final ResultSet rs = ((ResultSet) cs.getObject(2));
+                final StringBuilder sb = new StringBuilder();
+                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);
+                    final String text = rs.getString("text");
+                    if (text != null) {
+                        sb.append(text);
+                        sb.append('\n');
                     }
                 }
                 rs.close();
-                return null;
+                return sb.toString();
             }
         });
-    }
-
+    }    
+    
     private RealtimeReporterEvent convert(final String itemType, final String text) {
         logger.fine(() -> "\n---- " + itemType + " ----\n" + text);
         try {
diff --git a/sqldev/src/main/java/org/utplsql/sqldev/dal/UtplsqlDao.java b/sqldev/src/main/java/org/utplsql/sqldev/dal/UtplsqlDao.java
index be4d6d09..48bfc696 100644
--- a/sqldev/src/main/java/org/utplsql/sqldev/dal/UtplsqlDao.java
+++ b/sqldev/src/main/java/org/utplsql/sqldev/dal/UtplsqlDao.java
@@ -42,6 +42,7 @@ public class UtplsqlDao {
     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;
+    public static final int FETCH_ROWS = 100;
     private JdbcTemplate jdbcTemplate;
     // cache fields
     private Boolean cachedDbaViewAccessible;
@@ -50,6 +51,7 @@ public class UtplsqlDao {
 
     public UtplsqlDao(final Connection conn) {
         jdbcTemplate = new JdbcTemplate(new SingleConnectionDataSource(conn, true));
+        jdbcTemplate.setFetchSize(FETCH_ROWS);
     }
 
     /**
@@ -918,17 +920,17 @@ public String htmlCodeCoverage(final List<String> pathList, final List<String> s
         sb.append("             a_paths => ut_varchar2_list(\n");
         sb.append(StringTools.getCSV(pathList, 16));
         sb.append("             ),\n");
-        if (!schemaList.isEmpty()) {
+        if (schemaList != null && !schemaList.isEmpty()) {
             sb.append("             a_coverage_schemes => ut_varchar2_list(\n");
             sb.append(StringTools.getCSV(schemaList, 16));
             sb.append("             ),\n");
         }
-        if (!includeObjectList.isEmpty()) {
+        if (includeObjectList != null && !includeObjectList.isEmpty()) {
             sb.append("             a_include_objects => ut_varchar2_list(\n");
             sb.append(StringTools.getCSV(includeObjectList, 16));
             sb.append("             ),\n");
         }
-        if (!excludeObjectList.isEmpty()) {
+        if (excludeObjectList != null && excludeObjectList.isEmpty()) {
             sb.append("             a_exclude_objects => ut_varchar2_list(\n");
             sb.append(StringTools.getCSV(excludeObjectList, 16));
             sb.append("             ),\n");
diff --git a/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlRunner.java b/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlRunner.java
index bf0628af..dbc62cb4 100644
--- a/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlRunner.java
+++ b/sqldev/src/main/java/org/utplsql/sqldev/runner/UtplsqlRunner.java
@@ -27,6 +27,7 @@
 
 import javax.swing.JFrame;
 
+import org.utplsql.sqldev.coverage.CodeCoverageReporter;
 import org.utplsql.sqldev.dal.RealtimeReporterDao;
 import org.utplsql.sqldev.dal.RealtimeReporterEventConsumer;
 import org.utplsql.sqldev.model.DatabaseTools;
@@ -48,26 +49,65 @@
 public class UtplsqlRunner implements RealtimeReporterEventConsumer {
     private static final Logger logger = Logger.getLogger(UtplsqlRunner.class.getName());
 
-    private List<String> pathList;
+    private final boolean withCodeCoverage;
+    private final List<String> pathList;
+    private final List<String> schemaList;
+    private final List<String> includeObjectList;
+    private final List<String> excludeObjectList;
     private String connectionName;
     private Connection producerConn;
     private Connection consumerConn;
-    private final String reporterId = UUID.randomUUID().toString().replace("-", "");
+    private final String realtimeReporterId = UUID.randomUUID().toString().replace("-", "");
+    private final String coverageReporterId =  UUID.randomUUID().toString().replace("-", "");
     private Run run;
     private RunnerPanel panel;
+    private JFrame frame; // for testing purposes only (outside of SQL Developer)
     private Thread producerThread;
     private Thread consumerThread;
 
     public UtplsqlRunner(final List<String> pathList, final String connectionName) {
+        this.withCodeCoverage = false;
         this.pathList = pathList;
+        this.schemaList = null;
+        this.includeObjectList = null;
+        this.excludeObjectList = null;
+        setConnection(connectionName);
+    }
+    
+    public UtplsqlRunner(final List<String> pathList, final List<String> schemaList,
+            final List<String> includeObjectList, final List<String> excludeObjectList, final String connectionName) {
+        this.withCodeCoverage = true;
+        this.pathList = pathList;
+        this.schemaList = schemaList;
+        this.includeObjectList = includeObjectList;
+        this.excludeObjectList = excludeObjectList;
         setConnection(connectionName);
     }
 
     /**
-     * this constructor is intended for tests only
+     * this constructor is intended for tests only (without code coverage)
      */
     public UtplsqlRunner(final List<String> pathList, final Connection producerConn, final Connection consumerConn) {
+        this.withCodeCoverage = false;
         this.pathList = pathList;
+        this.schemaList = null;
+        this.includeObjectList = null;
+        this.excludeObjectList = null;
+        this.producerConn = producerConn;
+        this.consumerConn = consumerConn;
+    }
+
+    /**
+     * this constructor is intended for tests only (with code coverage)
+     */
+    public UtplsqlRunner(final List<String> pathList, final List<String> schemaList,
+            final List<String> includeObjectList, final List<String> excludeObjectList, final Connection producerConn,
+            final Connection consumerConn) {
+        this.withCodeCoverage = true;
+        this.pathList = pathList;
+        this.schemaList = schemaList;
+        this.includeObjectList = includeObjectList;
+        this.excludeObjectList = excludeObjectList;
         this.producerConn = producerConn;
         this.consumerConn = consumerConn;
     }
@@ -86,6 +126,9 @@ public void dispose() {
         // running in SQL Developer
         DatabaseTools.closeConnection(producerConn);
         DatabaseTools.closeConnection(consumerConn);
+        if (frame != null) {
+            frame.setVisible(false);
+        }
     }
 
     @Override
@@ -116,7 +159,7 @@ private String getSysdate() {
     }
 
     private void initRun() {
-        run = new Run(reporterId, connectionName, pathList);
+        run = new Run(realtimeReporterId, connectionName, pathList);
         run.setStartTime(getSysdate());
         run.getCounter().setDisabled(0);
         run.getCounter().setSuccess(0);
@@ -128,14 +171,14 @@ private void initRun() {
         run.setCurrentTestNumber(0);
         run.setStatus(UtplsqlResources.getString("RUNNER_INITIALIZING_TEXT"));
         panel.setModel(run);
-        panel.update(reporterId);
+        panel.update(realtimeReporterId);
     }
     
     private void doProcess(final PreRunEvent event) {
         run.setTotalNumberOfTests(event.getTotalNumberOfTests());
         run.put(event.getItems());
         run.setStatus(UtplsqlResources.getString("RUNNER_RUNNING_TEXT"));
-        panel.update(reporterId);
+        panel.update(realtimeReporterId);
     }
 
     private void doProcess(final PostRunEvent event) {
@@ -145,7 +188,7 @@ private void doProcess(final PostRunEvent event) {
         run.setErrorStack(event.getErrorStack());
         run.setServerOutput(event.getServerOutput());
         run.setStatus(UtplsqlResources.getString("RUNNER_FINNISHED_TEXT"));
-        panel.update(reporterId);
+        panel.update(realtimeReporterId);
     }
 
     private void doProcess(final PreSuiteEvent event) {
@@ -189,7 +232,7 @@ private void doProcess(final PostSuiteEvent event) {
             sb.append(event.getServerOutput());
             test.setServerOutput(sb.toString());
         }
-        panel.update(reporterId);
+        panel.update(realtimeReporterId);
     }
 
     private void doProcess(final PreTestEvent event) {
@@ -203,7 +246,7 @@ private void doProcess(final PreTestEvent event) {
         run.setStatus(event.getId() + "...");
         run.setCurrentTestNumber(event.getTestNumber());
         run.setCurrentTest(test);
-        panel.update(reporterId);
+        panel.update(realtimeReporterId);
     }
 
     private void doProcess(final PostTestEvent event) {
@@ -234,29 +277,37 @@ private void doProcess(final PostTestEvent event) {
         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);
+        panel.update(realtimeReporterId);
     }
 
     private void produce() {
         try {
-            logger.fine(() -> "Running utPLSQL tests and producing events via reporter id " + reporterId + "...");
+            logger.fine(() -> "Running utPLSQL tests and producing events via reporter id " + realtimeReporterId + "...");
             final RealtimeReporterDao dao = new RealtimeReporterDao(producerConn);
-            dao.produceReport(reporterId, pathList);
-            logger.fine(() -> "All events produced for reporter id " + reporterId + ".");
+            if (withCodeCoverage) {
+                dao.produceReportWithCoverage(realtimeReporterId, coverageReporterId, pathList, schemaList, includeObjectList, excludeObjectList);
+            } else {
+                dao.produceReport(realtimeReporterId, pathList);
+            }
+            logger.fine(() -> "All events produced for reporter id " + realtimeReporterId + ".");
         } catch (Exception e) {
-            logger.severe(() -> "Error while producing events for reporter id " + reporterId + ": "
+            logger.severe(() -> "Error while producing events for reporter id " + realtimeReporterId + ": "
                     + (e != null ? e.getMessage() : "???"));
         }
     }
 
     private void consume() {
         try {
-            logger.fine(() -> "Consuming events from reporter id " + reporterId + " in realtime...");
+            logger.fine(() -> "Consuming events from reporter id " + realtimeReporterId + " in realtime...");
             final RealtimeReporterDao dao = new RealtimeReporterDao(consumerConn);
-            dao.consumeReport(reporterId, this);
+            dao.consumeReport(realtimeReporterId, this);
             logger.fine(() -> "All events consumed.");
+            if (withCodeCoverage) {
+                String html = dao.getHtmlCoverage(coverageReporterId);
+                CodeCoverageReporter.openInBrowser(html);
+            }
         } catch (Exception e) {
-            logger.severe(() -> "Error while consuming events for reporter id " + reporterId + ": "
+            logger.severe(() -> "Error while consuming events for reporter id " + realtimeReporterId + ": "
                     + (e != null ? e.getMessage() : "???"));
         }
         if (run.getTotalNumberOfTests() < 0) {
@@ -264,7 +315,7 @@ private void consume() {
             run.setExecutionTime(Double.valueOf(System.currentTimeMillis() - Double.valueOf(run.getStart())) / 1000);
             run.setEndTime(getSysdate());
             run.setTotalNumberOfTests(0);
-            panel.update(reporterId);
+            panel.update(realtimeReporterId);
         }
         if (isRunningInSqlDeveloper()) {
             dispose();
@@ -285,7 +336,7 @@ private boolean initGUI() {
                 RunnerFactory.showDockable();
                 panel = dockable.getRunnerPanel();
             } else {
-                final JFrame frame = new JFrame("utPLSQL Runner Panel");
+                frame = new JFrame("utPLSQL Runner Panel");
                 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
                 panel = new RunnerPanel();
                 frame.add(panel.getGUI());
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
index 029c8264..525e5516 100644
--- a/sqldev/src/main/java/org/utplsql/sqldev/ui/coverage/CodeCoverageReporterDialog.java
+++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/coverage/CodeCoverageReporterDialog.java
@@ -86,7 +86,7 @@ public CodeCoverageReporterDialog(final CodeCoverageReporter reporter) {
         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_SCHEMAS_LABEL"), reporter.getSchemas(), 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);
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
index 783b6415..a7650ea1 100644
--- a/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerPanel.java
+++ b/sqldev/src/main/java/org/utplsql/sqldev/ui/runner/RunnerPanel.java
@@ -26,11 +26,14 @@
 import java.awt.event.ActionListener;
 import java.awt.event.MouseAdapter;
 import java.awt.event.MouseEvent;
+import java.sql.Connection;
 import java.util.ArrayList;
+import java.util.HashSet;
 import java.util.List;
 import java.util.Map;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
+import java.util.stream.Collectors;
 
 import javax.swing.BorderFactory;
 import javax.swing.Box;
@@ -65,6 +68,7 @@
 import javax.swing.table.TableRowSorter;
 
 import org.springframework.web.util.HtmlUtils;
+import org.utplsql.sqldev.coverage.CodeCoverageReporter;
 import org.utplsql.sqldev.dal.UtplsqlDao;
 import org.utplsql.sqldev.model.DatabaseTools;
 import org.utplsql.sqldev.model.LimitedLinkedHashMap;
@@ -113,6 +117,7 @@ public class RunnerPanel {
     private JTable testOverviewTable;
     private JMenuItem testOverviewRunMenuItem;
     private JMenuItem testOverviewRunWorksheetMenuItem;
+    private JMenuItem testOverviewCodeCoverageMenuItem;
     private JCheckBoxMenuItem showTestDescriptionCheckBoxMenuItem;
     private JCheckBoxMenuItem showWarningIndicatorCheckBoxMenuItem;
     private JCheckBoxMenuItem showInfoIndicatorCheckBoxMenuItem;
@@ -196,6 +201,7 @@ private void resetDerived() {
         testOverviewTable.getRowSorter().setSortKeys(null);
         testOverviewRunMenuItem.setEnabled(false);
         testOverviewRunWorksheetMenuItem.setEnabled(false);
+        testOverviewCodeCoverageMenuItem.setEnabled(false);
         testIdTextArea.setText(null);
         testOwnerTextField.setText(null);
         testPackageTextField.setText(null);
@@ -652,6 +658,42 @@ private JPanel makeLabelledCounterComponent(final JLabel label, final JComponent
         groupPanel.setPreferredSize(dim);
         return groupPanel;
     }
+    
+    private void runCodeCoverage(boolean selectedOnly) {
+        final Connection conn = DatabaseTools.getConnection(currentRun.getConnectionName());
+        final UtplsqlDao dao = new UtplsqlDao(conn);
+        final List<String> pathList = new ArrayList<>();
+        final HashSet<String> testPackages = new HashSet<>();
+        if (selectedOnly) {
+            // pathList and unique testPackages based on selected tests
+            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);
+                testPackages.add(test.getOwnerName() + "." + test.getObjectName());
+            }
+        } else {
+            // pathList and unique testPackages based on currentRun
+            pathList.addAll(currentRun.getPathList());
+            for (Test t : currentRun.getTests().values()) {
+                testPackages.add(t.getOwnerName() + "." + t.getObjectName());
+            }
+        } 
+        // add dependencies of every test package (one DB call per test package)
+        final HashSet<String> includeObjects = new HashSet<>();
+        for (String testPackage : testPackages) {
+            String[] obj = testPackage.split("\\.");
+            includeObjects.addAll(dao.includes(obj[0], obj[1]));
+        }
+        // remove test packages
+        for (String testPackage : testPackages) {
+            includeObjects.remove(testPackage.toUpperCase());
+        }
+        final CodeCoverageReporter reporter = new CodeCoverageReporter(pathList,
+                includeObjects.stream().sorted().collect(Collectors.toList()), currentRun.getConnectionName());
+        reporter.showParameterWindow();
+    }
 
     private void initializeGUI() {
         // Base panel containing all components 
@@ -689,6 +731,11 @@ private void initializeGUI() {
             worksheet.runTestAsync();
         });
         toolbar.add(rerunWorksheetButton);
+        final ToolbarButton codeCoverageButton = new ToolbarButton(UtplsqlResources.getIcon("CODE_COVERAGE_ICON"));
+        codeCoverageButton.setToolTipText(UtplsqlResources.getString("RUNNER_CODE_COVERAGE_TOOLTIP"));
+        codeCoverageButton.setBorder(buttonBorder);
+        codeCoverageButton.addActionListener(event -> runCodeCoverage(false));
+        toolbar.add(codeCoverageButton);
         toolbar.add(Box.createHorizontalGlue());
         runComboBoxModel = new DefaultComboBoxModel<>();
         runComboBox = new JComboBox<>(runComboBoxModel);
@@ -878,6 +925,7 @@ private void initializeGUI() {
                 syncDetailTab();
                 testOverviewRunMenuItem.setEnabled(true);
                 testOverviewRunWorksheetMenuItem.setEnabled(true);
+                testOverviewCodeCoverageMenuItem.setEnabled(true);
             }
         });
         testOverviewTable.addMouseListener(new MouseAdapter() {
@@ -946,6 +994,9 @@ public Component getTableCellRendererComponent(final JTable table, final Object
             worksheet.runTestAsync();
         });
         testOverviewPopupMenu.add(testOverviewRunWorksheetMenuItem);
+        testOverviewCodeCoverageMenuItem = new JMenuItem(UtplsqlResources.getString("MENU_CODE_COVERAGE_LABEL"), UtplsqlResources.getIcon("CODE_COVERAGE_ICON"));
+        testOverviewCodeCoverageMenuItem.addActionListener(event -> runCodeCoverage(true));
+        testOverviewPopupMenu.add(testOverviewCodeCoverageMenuItem);
         testOverviewPopupMenu.add(new JSeparator());
         showSuccessfulTestsCheckBoxMenuItem = new JCheckBoxMenuItem(UtplsqlResources.getString("PREF_SHOW_SUCCESSFUL_TESTS_LABEL").replace("?", ""), true);
         showSuccessfulTestsCheckBoxMenuItem.addActionListener(event -> {
diff --git a/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources.properties b/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources.properties
index 9c0f4c08..141984bf 100644
--- a/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources.properties
+++ b/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources.properties
@@ -22,6 +22,7 @@ CHECKMARK_ICON=/org/utplsql/sqldev/resources/images/checkmark.png
 STATUS_ICON=/org/utplsql/sqldev/resources/images/status.png
 # progress.gif - the animated version - does not work
 PROGRESS_ICON=/org/utplsql/sqldev/resources/images/progress.png
+CODE_COVERAGE_ICON=/org/utplsql/sqldev/resources/images/coverage.png
 
 # Translatable text
 PREF_LABEL=utPLSQL
@@ -79,6 +80,7 @@ RUNNER_REFRESH_TOOLTIP=Reset ordering and refresh
 RUNNER_RERUN_TOOLTIP=Rerun all tests
 RUNNER_CLEAR_BUTTON=Clear run history
 RUNNER_RERUN_WORKSHEET_TOOLTIP=Rerun all tests in a new worksheet
+RUNNER_CODE_COVERAGE_TOOLTIP=Rerun all tests with code coverage
 RUNNER_TESTS_LABEL=Tests
 RUNNER_FAILURES_LABEL=Failures
 RUNNER_ERRORS_LABEL=Errors
diff --git a/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources_de.properties b/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources_de.properties
index a18feede..44bcbdcf 100644
--- a/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources_de.properties
+++ b/sqldev/src/main/resources/org/utplsql/sqldev/resources/UtplsqlResources_de.properties
@@ -55,6 +55,7 @@ RUNNER_VIEW_TITLE=utPLSQL
 RUNNER_REFRESH_TOOLTIP=Sortierung zur�cksetzen und aktualisieren
 RUNNER_RERUN_TOOLTIP=Alle Tests erneut ausf�hren
 RUNNER_RERUN_WORKSHEET_TOOLTIP=Alle Tests in einem neuen Arbeitsblatt erneut ausf�hren
+RUNNER_CODE_COVERAGE_TOOLTIP=Alle Tests mit Codeabdeckung ausf�hren
 RUNNER_CLEAR_BUTTON=Run History l�schen
 RUNNER_TESTS_LABEL=Tests
 RUNNER_FAILURES_LABEL=Fehlschl�ge
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
index 6aa90a94..72a8476f 100644
--- a/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterTest.java
+++ b/sqldev/src/test/java/org/utplsql/sqldev/test/coverage/CodeCoverageReporterTest.java
@@ -20,17 +20,15 @@
 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.After;
 import org.junit.Assert;
-import org.junit.BeforeClass;
+import org.junit.Before;
 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;
@@ -40,8 +38,8 @@
 
 public class CodeCoverageReporterTest extends AbstractJdbcTest {
 
-    @BeforeClass
-    public static void setup() {
+    @Before
+    public void setup() {
         StringBuilder sb = new StringBuilder();
         sb.append("CREATE OR REPLACE FUNCTION f RETURN INTEGER IS\n");
         sb.append("BEGIN\n");
@@ -98,28 +96,48 @@ private Path getNewestOutputFile() {
 
     @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<String> pathList = Arrays.asList(":test_f");
         final List<String> includeObjectList = Arrays.asList("f");
-        final CodeCoverageReporter reporter = new CodeCoverageReporter(pathList, includeObjectList, conn);
+        final CodeCoverageReporter reporter = new CodeCoverageReporter(pathList, includeObjectList, DatabaseTools.getConnection(dataSource));
         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("<h3>SCOTT.F</h3><h4><span class=\"green\">100 %</span> lines covered</h4>"));
     }
+    
+    @Test
+    public void defaultSchemaCalculationMixedCase() {
+        final CodeCoverageReporter reporter = new CodeCoverageReporter(Arrays.asList(":something"),
+                Arrays.asList("scott.a", "scott.b", "hR.a", "HR.B", "hr.c"), DatabaseTools.getConnection(dataSource));
+        Assert.assertEquals("HR, SCOTT", reporter.getSchemas());
+    }
+
+    @Test
+    public void defaultSchemaCalculationWithoutIncludeObjects() {
+        final CodeCoverageReporter reporter = new CodeCoverageReporter(Arrays.asList(":something"),
+                Arrays.asList(), DatabaseTools.getConnection(dataSource));
+        Assert.assertEquals(null, reporter.getSchemas());
+    }
 
-    @AfterClass
-    public static void teardown() {
+    @Test
+    public void defaultSchemaCalculationWithoutOwnerInformation() {
+        final CodeCoverageReporter reporter = new CodeCoverageReporter(Arrays.asList(":something"),
+                Arrays.asList("a", "b", "c"), DatabaseTools.getConnection(dataSource));
+        Assert.assertEquals("", reporter.getSchemas());
+    }
+
+    @Test
+    public void defaultSchemaCalculationWithJustOneOwner() {
+        final CodeCoverageReporter reporter = new CodeCoverageReporter(Arrays.asList(":something"),
+                Arrays.asList("a", "b", "scott.c"), DatabaseTools.getConnection(dataSource));
+        Assert.assertEquals("SCOTT", reporter.getSchemas());
+    }
+    
+    @After
+    public void teardown() {
         executeAndIgnore(jdbcTemplate, "DROP PACKAGE test_f");
         executeAndIgnore(jdbcTemplate, "DROP FUNCTION f");
     }
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
index ee975abd..e88b5857 100644
--- a/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterTest.java
+++ b/sqldev/src/test/java/org/utplsql/sqldev/test/dal/RealtimeReporterTest.java
@@ -32,6 +32,7 @@
 import org.utplsql.sqldev.model.runner.PreSuiteEvent;
 import org.utplsql.sqldev.model.runner.PreTestEvent;
 import org.utplsql.sqldev.test.AbstractJdbcTest;
+import org.utplsql.sqldev.test.coverage.CodeCoverageReporterTest;
 
 public class RealtimeReporterTest extends AbstractJdbcTest {
     private static final Logger logger = Logger.getLogger(RealtimeReporterTest.class.getName());
@@ -142,13 +143,16 @@ public void setup() {
         sb.append("    END;\n");
         sb.append("END;");
         jdbcTemplate.execute(sb.toString());
-    }
 
+        new CodeCoverageReporterTest().setup();
+    }
+    
     @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");
+        new CodeCoverageReporterTest().teardown();
     }
 
     @Test
@@ -168,4 +172,18 @@ public void produceAndConsume() {
         Assert.assertEquals(7, consumer.getConsumedList().stream().filter(it -> it instanceof PostTestEvent).count());
         Assert.assertEquals(28, consumer.getConsumedList().size());
     }
+    
+    @Test
+    public void produceAndConsumeWithCoverage() {
+        final RealtimeReporterDao dao = new RealtimeReporterDao(DatabaseTools.getConnection(dataSource));
+        final String realtimeReporterId = UUID.randomUUID().toString().replace("-", "");
+        final String coverageReporterId = UUID.randomUUID().toString().replace("-", "");
+        final TestRealtimerReporterEventConsumer consumer = new TestRealtimerReporterEventConsumer();
+        dao.produceReportWithCoverage(realtimeReporterId, coverageReporterId, Arrays.asList(":test_f"), null, null, null);
+        dao.consumeReport(realtimeReporterId, consumer);
+        logger.fine(consumer.getConsumedList().toString());
+        Assert.assertEquals(6, consumer.getConsumedList().size());
+        final String html = dao.getHtmlCoverage(coverageReporterId);
+        Assert.assertTrue(html.trim().endsWith("</html>"));
+    }
 }
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
index c3531381..eba5aec7 100644
--- a/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerTest.java
+++ b/sqldev/src/test/java/org/utplsql/sqldev/test/runner/UtplsqlRunnerTest.java
@@ -27,6 +27,7 @@
 import org.utplsql.sqldev.model.SystemTools;
 import org.utplsql.sqldev.runner.UtplsqlRunner;
 import org.utplsql.sqldev.test.AbstractJdbcTest;
+import org.utplsql.sqldev.test.coverage.CodeCoverageReporterTest;
 
 public class UtplsqlRunnerTest extends AbstractJdbcTest {
 
@@ -108,11 +109,13 @@ public void setup() {
         sb.append("   END;\n");
         sb.append("END;");
         jdbcTemplate.execute(sb.toString());
+        new CodeCoverageReporterTest().setup();
     }
 
     @After
     public void teardown() {
         executeAndIgnore(jdbcTemplate, "DROP PACKAGE junit_utplsql_test1_pkg");
+        new CodeCoverageReporterTest().teardown();
     }
 
     @Test
@@ -140,4 +143,30 @@ public void runTestsWithMaxTime() {
         Assert.assertNotNull(runner);
         runner.dispose();
     }
+
+    @Test
+    public void runTestsWithCodeCoverage() {
+        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(":test_f"), null, null, null, producerConn, consumerConn);
+        runner.runTestAsync();
+
+        SystemTools.waitForThread(runner.getProducerThread(), 200000);
+        SystemTools.waitForThread(runner.getConsumerThread(), 200000);
+        SystemTools.sleep(4 * 1000);
+        Assert.assertNotNull(runner);
+        runner.dispose();
+    }
 }