From 2a914454bc2f118f3e95c7ebf76eb73593f79db9 Mon Sep 17 00:00:00 2001 From: Mykhailo Shepelenko Date: Sat, 19 Apr 2025 18:57:12 +0200 Subject: [PATCH 01/10] static version of checked code --- web/server/vue-cli/config/webpack.dev.js | 2 +- .../Report/CoverageVisualization.vue | 206 ++++++++++++++++ .../Report/CoverageVisualizationTest.vue | 54 +++++ .../vue-cli/src/components/Report/Report.vue | 154 +++++++++++- .../components/Report/coverage-static.html | 183 ++++++++++++++ .../src/components/Report/coverage-test.html | 224 ++++++++++++++++++ web/server/vue-cli/src/router/index.js | 2 +- web/server/vue-cli/src/views/CoverageTest.vue | 21 ++ 8 files changed, 841 insertions(+), 5 deletions(-) create mode 100644 web/server/vue-cli/src/components/Report/CoverageVisualization.vue create mode 100644 web/server/vue-cli/src/components/Report/CoverageVisualizationTest.vue create mode 100644 web/server/vue-cli/src/components/Report/coverage-static.html create mode 100644 web/server/vue-cli/src/components/Report/coverage-test.html create mode 100644 web/server/vue-cli/src/views/CoverageTest.vue diff --git a/web/server/vue-cli/config/webpack.dev.js b/web/server/vue-cli/config/webpack.dev.js index dd8fc4be69..a128761613 100644 --- a/web/server/vue-cli/config/webpack.dev.js +++ b/web/server/vue-cli/config/webpack.dev.js @@ -24,7 +24,7 @@ module.exports = merge(common, { }, devtool: 'inline-source-map', devServer: { - port: 8080, + port: 8081, hot: true, historyApiFallback: { // If the URL contains a product endpoint and we server a static file diff --git a/web/server/vue-cli/src/components/Report/CoverageVisualization.vue b/web/server/vue-cli/src/components/Report/CoverageVisualization.vue new file mode 100644 index 0000000000..244b4ba198 --- /dev/null +++ b/web/server/vue-cli/src/components/Report/CoverageVisualization.vue @@ -0,0 +1,206 @@ + + + + + \ No newline at end of file diff --git a/web/server/vue-cli/src/components/Report/CoverageVisualizationTest.vue b/web/server/vue-cli/src/components/Report/CoverageVisualizationTest.vue new file mode 100644 index 0000000000..ab59815cc1 --- /dev/null +++ b/web/server/vue-cli/src/components/Report/CoverageVisualizationTest.vue @@ -0,0 +1,54 @@ + + + + + \ No newline at end of file diff --git a/web/server/vue-cli/src/components/Report/Report.vue b/web/server/vue-cli/src/components/Report/Report.vue index 503a30c8ab..780e22ed59 100644 --- a/web/server/vue-cli/src/components/Report/Report.vue +++ b/web/server/vue-cli/src/components/Report/Report.vue @@ -265,6 +265,26 @@ /> + +
+
+ + mdi-chart-bar + +
+ +
@@ -317,6 +337,7 @@ import SelectSameReport from "./SelectSameReport"; import { ReportInfoButton, ShowReportInfoDialog } from "./ReportInfo"; import ReportStepMessage from "./ReportStepMessage"; +import CoverageVisualization from "./CoverageVisualization.vue"; const ReportStepMessageClass = Vue.extend(ReportStepMessage); export default { @@ -332,7 +353,8 @@ export default { SetCleanupPlanBtn, ShowReportInfoDialog, ToggleBlameViewBtn, - UserIcon + UserIcon, + CoverageVisualization }, directives: { FillHeight }, mixins: [ GitBlameMixin ], @@ -364,9 +386,49 @@ export default { annotation: null, selectedChecker: null, analysisInfoDialog: false, - reportId: null, + reportId: "test-report-123", enableBlameView, - docUrl: null + docUrl: null, + showCoverage: true, + coverageData: { + totalLines: 100, + coveredLines: 75, + lineCoverage: { + 1: 1, // covered + 2: 1, // covered + 3: 0, // uncovered + 4: 1, // covered + 5: 0.5, // partially covered + 6: 1, // covered + 7: 0, // uncovered + 8: 1, // covered + 9: 0, // uncovered + 10: 1 // covered + } + }, + currentFile: "test.cpp", + fileContent: [ + "#include ", + "", + "int main() {", + " int x = 5; // Line 1 - covered", + " int y = 10; // Line 2 - covered", + " ", + " if (x > 0) { // Line 3 - uncovered", + " x++; // Line 4 - covered", + " }", + " ", + " y = x * 2; // Line 5 - partially covered", + " ", + " std::cout << \"x: \" << x << std::endl; // Line 6 - covered", + " ", + " if (y < 0) { // Line 7 - uncovered", + " y = 0; // Line 8 - covered", + " }", + " ", + " return 0; // Line 9 - uncovered", + "} // Line 10 - covered" + ] }; }, @@ -455,6 +517,31 @@ export default { }); this.editor.setSize("100%", "100%"); + // Add test code + const testCode = `#include + +int main() { + int x = 5; // Line 1 - covered + int y = 10; // Line 2 - covered + + if (x > 0) { // Line 3 - uncovered + x++; // Line 4 - covered + } + + y = x * 2; // Line 5 - partially covered + + std::cout << "x: " << x << std::endl; // Line 6 - covered + + if (y < 0) { // Line 7 - uncovered + y = 0; // Line 8 - covered + } + + return 0; // Line 9 - uncovered +} // Line 10 - covered`; + + this.editor.setValue(testCode); + this.updateCodeMirrorCoverage(); + this.editor.on("viewportChange", (cm, from, to) => { this.drawLines(from, to); }); @@ -489,6 +576,8 @@ export default { checkerId: this.report.checkerId }); }); + + this.loadCoverageData(); }, methods: { @@ -914,6 +1003,42 @@ export default { openAnalysisInfoDialog() { this.reportId = this.report.reportId; this.analysisInfoDialog = true; + }, + + async loadCoverageData() { + this.updateCodeMirrorCoverage(); + }, + updateCodeMirrorCoverage() { + if (!this.editor) return; + + const doc = this.editor.getDoc(); + const lineCoverage = this.coverageData.lineCoverage; + + doc.eachLine((line, lineNo) => { + const coverage = lineCoverage[lineNo + 1]; + if (coverage) { + const className = coverage === 1 ? "covered-line" : + coverage === 0 ? "uncovered-line" : "partial-line"; + doc.addLineClass(lineNo, "background", className); + } + }); + }, + toggleCoverageView(show) { + this.showCoverage = show; + if (this.editor) { + const doc = this.editor.getDoc(); + doc.eachLine((line, lineNo) => { + doc.removeLineClass(lineNo, "background", "covered-line"); + doc.removeLineClass(lineNo, "background", "uncovered-line"); + doc.removeLineClass(lineNo, "background", "partial-line"); + }); + if (show) { + this.updateCodeMirrorCoverage(); + } + } + }, + toggleCoverage() { + this.showCoverage = !this.showCoverage; } } }; @@ -998,4 +1123,27 @@ export default { opacity: 0.7; font-weight: lighter; } + +.covered-line { + background-color: rgba(0, 255, 0, 0.1); +} + +.uncovered-line { + background-color: rgba(255, 0, 0, 0.1); +} + +.partial-line { + background-color: rgba(255, 165, 0, 0.1); +} + +.toolbar { + display: flex; + align-items: center; + padding: 4px; + border-bottom: 1px solid #e0e0e0; +} + +.toolbar button { + margin-right: 8px; +} \ No newline at end of file diff --git a/web/server/vue-cli/src/components/Report/coverage-static.html b/web/server/vue-cli/src/components/Report/coverage-static.html new file mode 100644 index 0000000000..023b7569c5 --- /dev/null +++ b/web/server/vue-cli/src/components/Report/coverage-static.html @@ -0,0 +1,183 @@ + + + + + + Code Coverage Visualization + + + + + + +
+
+
+

Code Coverage

+ +
+ +
+
+
+
{{ coverageData.totalLines }}
+
Total Lines
+
+
+
{{ coverageData.coveredLines }}
+
Covered Lines
+
+
+
{{ coveragePercentage }}%
+
Coverage
+
+
+ +
+
+
+
+
+ +
+
+ {{ index + 1 }} + {{ line }} +
+
+
+
+
+ + + + + + \ No newline at end of file diff --git a/web/server/vue-cli/src/components/Report/coverage-test.html b/web/server/vue-cli/src/components/Report/coverage-test.html new file mode 100644 index 0000000000..3ebb61cc0b --- /dev/null +++ b/web/server/vue-cli/src/components/Report/coverage-test.html @@ -0,0 +1,224 @@ + + + + Coverage Visualization Test + + + + + + + + +
+ + + + +
+
+
+ Test Coverage +
+ +
+
+
+ {{ coverageData.totalLines }} + Total Lines +
+
+ {{ coverageData.coveredLines }} + Covered Lines +
+
+ {{ coveragePercentage }}% + Coverage +
+
+
+
+
+
+ +
+
+ test.cpp +
+
+
+ {{ index + 1 }} + {{ line }} +
+
+
+
+
+
+
+
+ + + + \ No newline at end of file diff --git a/web/server/vue-cli/src/router/index.js b/web/server/vue-cli/src/router/index.js index 3a081da8a1..93c2851cec 100644 --- a/web/server/vue-cli/src/router/index.js +++ b/web/server/vue-cli/src/router/index.js @@ -132,6 +132,6 @@ CheckerCoverageStatistics"), component: () => import("@/views/SourceComponent") }, ] - } + }, ] }); \ No newline at end of file diff --git a/web/server/vue-cli/src/views/CoverageTest.vue b/web/server/vue-cli/src/views/CoverageTest.vue new file mode 100644 index 0000000000..b092472de0 --- /dev/null +++ b/web/server/vue-cli/src/views/CoverageTest.vue @@ -0,0 +1,21 @@ + + + \ No newline at end of file From bf12aef8cb1ae4437022a877c86857739a07b412 Mon Sep 17 00:00:00 2001 From: Mykhailo Shepelenko Date: Sun, 20 Apr 2025 11:28:36 +0200 Subject: [PATCH 02/10] lines that covered by analyzer are highlited, percentage of covered lines is shown --- server_config.json | 13 + web/server/codechecker_server/cli/server.py | 7 +- web/server/codechecker_server/server.py | 45 +- web/server/vue-cli/config/webpack.dev.js | 2 +- web/server/vue-cli/nightwatch.conf.js | 2 +- .../Report/CoverageVisualization.vue | 206 ------- .../Report/CoverageVisualizationTest.vue | 54 -- .../vue-cli/src/components/Report/Report.vue | 570 ++++++++++++------ .../components/Report/coverage-static.html | 183 ------ .../src/components/Report/coverage-test.html | 224 ------- .../vue-cli/src/services/api/cc.service.js | 41 ++ web/server/vue-cli/src/views/ReportDetail.vue | 129 +++- 12 files changed, 572 insertions(+), 904 deletions(-) create mode 100644 server_config.json delete mode 100644 web/server/vue-cli/src/components/Report/CoverageVisualization.vue delete mode 100644 web/server/vue-cli/src/components/Report/CoverageVisualizationTest.vue delete mode 100644 web/server/vue-cli/src/components/Report/coverage-static.html delete mode 100644 web/server/vue-cli/src/components/Report/coverage-test.html diff --git a/server_config.json b/server_config.json new file mode 100644 index 0000000000..ac8ebe20a2 --- /dev/null +++ b/server_config.json @@ -0,0 +1,13 @@ +{ + "view_port": 8001, + "workspace": "/Users/shepelenkomykhailo/.codechecker", + "products": { + "Default": { + "connection": "sqlite", + "database": "/Users/shepelenkomykhailo/.codechecker/Default.sqlite" + } + }, + "server": { + "max_workers": 1 + } +} \ No newline at end of file diff --git a/web/server/codechecker_server/cli/server.py b/web/server/codechecker_server/cli/server.py index c7781250b8..dd91a7f841 100644 --- a/web/server/codechecker_server/cli/server.py +++ b/web/server/codechecker_server/cli/server.py @@ -33,7 +33,7 @@ from codechecker_report_converter import twodim from codechecker_common import arg, cmd_config, logger, util -from codechecker_common.compatibility.multiprocessing import Pool, cpu_count +from codechecker_common.compatibility.multiprocessing import cpu_count from codechecker_server import instance_manager, server from codechecker_server.database import database @@ -47,6 +47,8 @@ from codechecker_web.shared import webserver_context, database_status, \ host_check, env +import concurrent.futures + LOG = logger.get_logger('server') @@ -715,9 +717,10 @@ def _get_migration_decisions() -> List[Tuple[str, str, bool]]: failed_products: List[Tuple[str, DBStatus]] = [] thr_count = util.clamp(1, len(scheduled_upgrades_or_inits), cpu_count()) - with Pool(max_workers=thr_count) as executor: + with concurrent.futures.ProcessPoolExecutor(max_workers=thr_count) as executor: LOG.info("Initialising/upgrading products using %d concurrent " "jobs...", thr_count) + futures = [] for product_cfg, return_status in \ zip(scheduled_upgrades_or_inits, executor.map( # Bind the first 2 non-changing arguments of diff --git a/web/server/codechecker_server/server.py b/web/server/codechecker_server/server.py index f42d88739f..fb6647bfbf 100644 --- a/web/server/codechecker_server/server.py +++ b/web/server/codechecker_server/server.py @@ -67,6 +67,8 @@ from .database.database import DBSession from .database.run_db_model import IDENTIFIER as RUN_META, Run, RunLock +import concurrent.futures + LOG = get_logger('server') @@ -699,32 +701,7 @@ def cleanup_run_db(self): return True -def _do_db_cleanup(context, check_env, - id_: int, endpoint: str, display_name: str, - connection_str: str) -> Tuple[Optional[bool], str]: - # This functions is a concurrent job handler! - try: - prod = Product(id_, endpoint, display_name, connection_str, - context, check_env) - prod.connect(init_db=False) - if prod.db_status != DBStatus.OK: - status_str = database_status.db_status_msg.get(prod.db_status) - return None, \ - f"Cleanup not attempted, database status is \"{status_str}\"" - - prod.cleanup_run_db() - prod.teardown() - - # Result is hard-wired to True, because the db_cleanup routines - # swallow and log the potential errors but do not return them. - return True, "" - except Exception as e: - import traceback - traceback.print_exc() - return False, str(e) - - -def _do_db_cleanups(config_database, context, check_env) \ +def _do_db_cleanup(config_database, context, check_env) \ -> Tuple[bool, List[Tuple[str, str]]]: """ Performs on-demand start-up database cleanup on all the products present @@ -752,14 +729,16 @@ def _get_products() -> List[Product]: thr_count = util.clamp(1, len(products), cpu_count()) overall_result, failures = True, [] - with Pool(max_workers=thr_count) as executor: + with concurrent.futures.ProcessPoolExecutor(max_workers=thr_count) as executor: LOG.info("Performing database cleanup using %d concurrent jobs...", thr_count) - for product, result in \ - zip(products, executor.map( - partial(_do_db_cleanup, context, check_env), - *zip(*products))): - success, reason = result + futures = [] + for product in products: + futures.append(executor.submit( + partial(_do_db_cleanup, context, check_env), + *product)) + for future in concurrent.futures.as_completed(futures): + success, reason = future.result() if not success: _, endpoint, _, _ = product overall_result = False @@ -1148,7 +1127,7 @@ def check_callback_url_format(provider_name: str, callback_url: str): sys.exit(1) if not skip_db_cleanup: - all_success, fails = _do_db_cleanups(config_sql_server, + all_success, fails = _do_db_cleanup(config_sql_server, context, check_env) if not all_success: diff --git a/web/server/vue-cli/config/webpack.dev.js b/web/server/vue-cli/config/webpack.dev.js index a128761613..c585505fa3 100644 --- a/web/server/vue-cli/config/webpack.dev.js +++ b/web/server/vue-cli/config/webpack.dev.js @@ -14,7 +14,7 @@ const CC_SERVICE_ENDPOINTS = [ // Location of the Thrift API server. const CC_THRIFT_API_HOST = process.env.CC_THRIFT_API_HOST || 'http://localhost'; -const CC_THRIFT_API_PORT = process.env.CC_THRIFT_API_PORT || 8002; +const CC_THRIFT_API_PORT = process.env.CC_THRIFT_API_PORT || 8001; module.exports = merge(common, { mode: 'development', diff --git a/web/server/vue-cli/nightwatch.conf.js b/web/server/vue-cli/nightwatch.conf.js index dd810cabd4..3013b3724e 100644 --- a/web/server/vue-cli/nightwatch.conf.js +++ b/web/server/vue-cli/nightwatch.conf.js @@ -1,5 +1,5 @@ const host = process.env.HOST || "localhost"; -const port = process.env.PORT || 8002; +const port = process.env.PORT || 8001; const chromeHeadless = process.env.CHROME_HEADLESS; module.exports = { diff --git a/web/server/vue-cli/src/components/Report/CoverageVisualization.vue b/web/server/vue-cli/src/components/Report/CoverageVisualization.vue deleted file mode 100644 index 244b4ba198..0000000000 --- a/web/server/vue-cli/src/components/Report/CoverageVisualization.vue +++ /dev/null @@ -1,206 +0,0 @@ - - - - - \ No newline at end of file diff --git a/web/server/vue-cli/src/components/Report/CoverageVisualizationTest.vue b/web/server/vue-cli/src/components/Report/CoverageVisualizationTest.vue deleted file mode 100644 index ab59815cc1..0000000000 --- a/web/server/vue-cli/src/components/Report/CoverageVisualizationTest.vue +++ /dev/null @@ -1,54 +0,0 @@ - - - - - \ No newline at end of file diff --git a/web/server/vue-cli/src/components/Report/Report.vue b/web/server/vue-cli/src/components/Report/Report.vue index 780e22ed59..085f7fc37e 100644 --- a/web/server/vue-cli/src/components/Report/Report.vue +++ b/web/server/vue-cli/src/components/Report/Report.vue @@ -11,7 +11,8 @@ :cols="editorCols" > - + + + + + mdi-comment-multiple-outline + + Comments ({{ numOfComments }}) + + + + @@ -109,52 +133,53 @@ - + - - + + - - - + + - mdi-comment-multiple-outline - - Comments ({{ numOfComments }}) - + ({{ Math.round(coverageData.coveragePercentage) }}% covered) + + @@ -172,6 +197,22 @@ + + +
+ + mdi-file-outline + + + {{ sourceFile.filePath }} + +
+
+
+
- -
-
- - mdi-chart-bar - -
- -
@@ -337,7 +358,6 @@ import SelectSameReport from "./SelectSameReport"; import { ReportInfoButton, ShowReportInfoDialog } from "./ReportInfo"; import ReportStepMessage from "./ReportStepMessage"; -import CoverageVisualization from "./CoverageVisualization.vue"; const ReportStepMessageClass = Vue.extend(ReportStepMessage); export default { @@ -353,8 +373,7 @@ export default { SetCleanupPlanBtn, ShowReportInfoDialog, ToggleBlameViewBtn, - UserIcon, - CoverageVisualization + UserIcon }, directives: { FillHeight }, mixins: [ GitBlameMixin ], @@ -386,49 +405,12 @@ export default { annotation: null, selectedChecker: null, analysisInfoDialog: false, - reportId: "test-report-123", + reportId: null, enableBlameView, docUrl: null, - showCoverage: true, - coverageData: { - totalLines: 100, - coveredLines: 75, - lineCoverage: { - 1: 1, // covered - 2: 1, // covered - 3: 0, // uncovered - 4: 1, // covered - 5: 0.5, // partially covered - 6: 1, // covered - 7: 0, // uncovered - 8: 1, // covered - 9: 0, // uncovered - 10: 1 // covered - } - }, - currentFile: "test.cpp", - fileContent: [ - "#include ", - "", - "int main() {", - " int x = 5; // Line 1 - covered", - " int y = 10; // Line 2 - covered", - " ", - " if (x > 0) { // Line 3 - uncovered", - " x++; // Line 4 - covered", - " }", - " ", - " y = x * 2; // Line 5 - partially covered", - " ", - " std::cout << \"x: \" << x << std::endl; // Line 6 - covered", - " ", - " if (y < 0) { // Line 7 - uncovered", - " y = 0; // Line 8 - covered", - " }", - " ", - " return 0; // Line 9 - uncovered", - "} // Line 10 - covered" - ] + coverageData: null, + showCoverage: false, + coverageLoading: false, }; }, @@ -467,14 +449,29 @@ export default { await this.hideBlameView(); } - // Scroll to the current bug step item. - this.jumpTo( - this.treeItem.step?.startLine.toNumber() || - this.treeItem.report.line.toNumber()); + // Scroll to the current bug step item with null checks + if (this.treeItem) { + const line = this.treeItem.step?.startLine?.toNumber() || + this.treeItem.report?.line?.toNumber() || + 1; + this.jumpTo(line); + } }, - treeItem() { - this.init(this.treeItem); + treeItem: { + handler(newVal) { + if (!newVal) return; + + if (newVal.coverageData) { + console.log("New coverage data received:", newVal.coverageData); + this.coverageData = newVal.coverageData; + if (this.showCoverage) { + this.updateCoverageHighlighting(); + } + } + this.init(newVal); + }, + immediate: true }, showArrows() { @@ -485,13 +482,29 @@ export default { } }, - report() { - this.loadNumOfComments = true; - ccService.getClient().getCommentCount(this.report.reportId, - handleThriftError(numOfComments => { - this.numOfComments = numOfComments; - this.loadNumOfComments = false; - })); + report: { + handler(newVal) { + if (newVal) { + this.loadNumOfComments = true; + ccService.getClient().getCommentCount(newVal.reportId, + handleThriftError(numOfComments => { + this.numOfComments = numOfComments; + this.loadNumOfComments = false; + })); + this.loadCoverageData(); + } + }, + immediate: true + }, + + showCoverage: { + handler(newVal) { + if (newVal) { + this.updateCoverageHighlighting(); + } else { + this.clearCoverageHighlighting(); + } + } } }, @@ -510,38 +523,13 @@ export default { lineNumbers: true, readOnly: true, mode: "text/x-c++src", - gutters: [ "CodeMirror-linenumbers", "bugInfo" ], - extraKeys: {}, - viewportMargin: 200, - highlightSelectionMatches : { showToken: /\w/, annotateScrollbar: true } + theme: "default", + lineWrapping: true, + gutters: [ "CodeMirror-linenumbers", "CodeMirror-foldgutter" ], + foldGutter: true }); this.editor.setSize("100%", "100%"); - // Add test code - const testCode = `#include - -int main() { - int x = 5; // Line 1 - covered - int y = 10; // Line 2 - covered - - if (x > 0) { // Line 3 - uncovered - x++; // Line 4 - covered - } - - y = x * 2; // Line 5 - partially covered - - std::cout << "x: " << x << std::endl; // Line 6 - covered - - if (y < 0) { // Line 7 - uncovered - y = 0; // Line 8 - covered - } - - return 0; // Line 9 - uncovered -} // Line 10 - covered`; - - this.editor.setValue(testCode); - this.updateCodeMirrorCoverage(); - this.editor.on("viewportChange", (cm, from, to) => { this.drawLines(from, to); }); @@ -576,34 +564,36 @@ int main() { checkerId: this.report.checkerId }); }); - - this.loadCoverageData(); }, methods: { init(treeItem) { + if (!treeItem) return; + this.loading = true; if (treeItem.step) { this.loadReportStep(treeItem.report, { - stepId: this.treeItem.id, + stepId: treeItem.id, ...treeItem.step }); } else if (treeItem.data) { this.loadReportStep(treeItem.report, { - stepId: this.treeItem.id, + stepId: treeItem.id, ...treeItem.data }); - } else { + } else if (treeItem.report) { this.loadReport(treeItem.report); + } else { + this.loading = false; } }, async loadReportStep(report, { stepId, fileId, startLine }) { if (!this.report || - !this.report.reportId.equals(report.reportId) || - !this.sourceFile || - !fileId.equals(this.sourceFile.fileId) + !this.report.reportId.equals(report.reportId) || + !this.sourceFile || + !fileId.equals(this.sourceFile.fileId) ) { this.report = report; @@ -870,10 +860,10 @@ int main() { //the last bug path element, then we render the warning. if (this.sourceFile.fileId.equals(this.report.fileId) && - (events.length == 0 || - this.report.checkerMsg !== events[events.length-1].msg || - this.report.line.toNumber() != - events[events.length-1].startLine.toNumber()) + (events.length === 0 || + this.report.checkerMsg !== events[events.length-1].msg || + this.report.line.toNumber() !== + events[events.length-1].startLine.toNumber()) ){ const chkrmsg_data = { $id: 999, $message:this.report.checkerMsg, @@ -1006,40 +996,180 @@ int main() { }, async loadCoverageData() { - this.updateCodeMirrorCoverage(); + if (!this.report || !this.report.fileId) return; + + this.coverageLoading = true; + try { + // Mock data for testing + this.coverageData = { + fileId: this.report.fileId, + filePath: "example.c", + totalLines: 12, + coveredLines: 8, + uncoveredLines: 4, + coveragePercentage: 66.67, + lineCoverage: [ + { + lineNumber: 1, + covered: true, + executionCount: 1, + lastExecution: "2024-04-20T10:00:00Z" + }, + { + lineNumber: 2, + covered: true, + executionCount: 1, + lastExecution: "2024-04-20T10:00:00Z" + }, + { + lineNumber: 3, + covered: true, + executionCount: 1, + lastExecution: "2024-04-20T10:00:00Z" + }, + { + lineNumber: 4, + covered: false, + executionCount: 0, + lastExecution: null + }, // Uncovered - division by zero + { + lineNumber: 5, + covered: true, + executionCount: 1, + lastExecution: "2024-04-20T10:00:00Z" + }, + { + lineNumber: 6, + covered: true, + executionCount: 1, + lastExecution: "2024-04-20T10:00:00Z" + }, + { + lineNumber: 7, + covered: true, + executionCount: 1, + lastExecution: "2024-04-20T10:00:00Z" + }, + { + lineNumber: 8, + covered: false, + executionCount: 0, + lastExecution: null + }, // Uncovered - division by zero + { + lineNumber: 9, + covered: false, + executionCount: 0, + lastExecution: null + }, // Uncovered - division by zero + { + lineNumber: 10, + covered: true, + executionCount: 1, + lastExecution: "2024-04-20T10:00:00Z" + }, + { + lineNumber: 11, + covered: true, + executionCount: 1, + lastExecution: "2024-04-20T10:00:00Z" + }, + { + lineNumber: 12, + covered: true, + executionCount: 1, + lastExecution: "2024-04-20T10:00:00Z" + } + ] + }; + + // Original backend call commented out for testing + /* + const runIds = this.report.runId ? [ this.report.runId ] : []; + this.coverageData = await ccService.getCodeCoverage( + this.report.fileId, + runIds + ); + */ + + this.updateCoverageHighlighting(); + } catch (err) { + console.error("Failed to load coverage data:", err); + } finally { + this.coverageLoading = false; + } }, - updateCodeMirrorCoverage() { - if (!this.editor) return; - - const doc = this.editor.getDoc(); - const lineCoverage = this.coverageData.lineCoverage; - - doc.eachLine((line, lineNo) => { - const coverage = lineCoverage[lineNo + 1]; - if (coverage) { - const className = coverage === 1 ? "covered-line" : - coverage === 0 ? "uncovered-line" : "partial-line"; - doc.addLineClass(lineNo, "background", className); + + updateCoverageHighlighting() { + if (!this.coverageData || !this.showCoverage) return; + + console.group("Coverage Highlighting Update"); + console.log("Total lines in editor:", this.editor.lineCount()); + console.log("Coverage data:", this.coverageData); + + // Create a map for faster line lookup + const coverageMap = new Map( + this.coverageData.lineCoverage.map(line => [ line.lineNumber, line ]) + ); + + this.editor.operation(() => { + for (let i = 0; i < this.editor.lineCount(); i++) { + const lineData = coverageMap.get(i + 1); + if (lineData) { + let className; + if (lineData.partiallyCovered) { + className = "coverage-partial-line"; + } else { + className = lineData.covered + ? "coverage-covered-line" + : "coverage-uncovered-line"; + } + + console.log(`Line ${i + 1}:`, { + covered: lineData.covered, + partiallyCovered: lineData.partiallyCovered, + executionCount: lineData.executionCount, + lastExecution: lineData.lastExecution, + className + }); + + [ "wrap", "background", "gutter", "line" ].forEach(type => { + this.editor.addLineClass(i, type, className); + }); + } else { + console.log(`Line ${i + 1}: No coverage data available`); + } } }); + + console.groupEnd(); }, - toggleCoverageView(show) { - this.showCoverage = show; - if (this.editor) { - const doc = this.editor.getDoc(); - doc.eachLine((line, lineNo) => { - doc.removeLineClass(lineNo, "background", "covered-line"); - doc.removeLineClass(lineNo, "background", "uncovered-line"); - doc.removeLineClass(lineNo, "background", "partial-line"); - }); - if (show) { - this.updateCodeMirrorCoverage(); + + clearCoverageHighlighting() { + if (!this.editor) return; + + this.editor.operation(() => { + for (let i = 0; i < this.editor.lineCount(); i++) { + [ "wrap", "background", "gutter", "line" ].forEach(type => { + this.editor.removeLineClass(i, type, "coverage-covered-line"); + this.editor.removeLineClass(i, type, "coverage-uncovered-line"); + this.editor.removeLineClass(i, type, "coverage-partial-line"); + }); } - } + }); + }, + + getLineCoverageClass(lineNumber) { + if (!this.coverageData || !this.showCoverage) return ""; + + const lineInfo = this.coverageData.lineCoverage.find( + line => line.lineNumber === lineNumber + ); + + if (!lineInfo) return "coverage-unknown"; + return lineInfo.covered ? "coverage-covered" : "coverage-uncovered"; }, - toggleCoverage() { - this.showCoverage = !this.showCoverage; - } } }; @@ -1048,12 +1178,68 @@ int main() { .scrollbar-bug-annotation { background-color: red; } + +.coverage-covered-line { + background-color: rgba(0, 255, 0, 0.1) !important; +} + +.coverage-uncovered-line { + background-color: rgba(255, 0, 0, 0.1) !important; +} + +.coverage-partial-line { + background-color: rgba(255, 255, 0, 0.1) !important; +} + +.CodeMirror { + .coverage-covered-line { + background-color: rgba(0, 255, 0, 0.1) !important; + } + + .coverage-uncovered-line { + background-color: rgba(255, 0, 0, 0.1) !important; + } + + .coverage-partial-line { + background-color: rgba(255, 255, 0, 0.1) !important; + } + + pre.coverage-covered-line { + background-color: rgba(0, 255, 0, 0.1) !important; + } + + pre.coverage-uncovered-line { + background-color: rgba(255, 0, 0, 0.1) !important; + } + + .CodeMirror-line.coverage-covered-line { + background-color: rgba(0, 255, 0, 0.1) !important; + } + + .CodeMirror-line.coverage-uncovered-line { + background-color: rgba(255, 0, 0, 0.1) !important; + } +} \ No newline at end of file + diff --git a/web/server/vue-cli/src/components/Report/coverage-static.html b/web/server/vue-cli/src/components/Report/coverage-static.html deleted file mode 100644 index 023b7569c5..0000000000 --- a/web/server/vue-cli/src/components/Report/coverage-static.html +++ /dev/null @@ -1,183 +0,0 @@ - - - - - - Code Coverage Visualization - - - - - - -
-
-
-

Code Coverage

- -
- -
-
-
-
{{ coverageData.totalLines }}
-
Total Lines
-
-
-
{{ coverageData.coveredLines }}
-
Covered Lines
-
-
-
{{ coveragePercentage }}%
-
Coverage
-
-
- -
-
-
-
-
- -
-
- {{ index + 1 }} - {{ line }} -
-
-
-
-
- - - - - - \ No newline at end of file diff --git a/web/server/vue-cli/src/components/Report/coverage-test.html b/web/server/vue-cli/src/components/Report/coverage-test.html deleted file mode 100644 index 3ebb61cc0b..0000000000 --- a/web/server/vue-cli/src/components/Report/coverage-test.html +++ /dev/null @@ -1,224 +0,0 @@ - - - - Coverage Visualization Test - - - - - - - - -
- - - - -
-
-
- Test Coverage -
- -
-
-
- {{ coverageData.totalLines }} - Total Lines -
-
- {{ coverageData.coveredLines }} - Covered Lines -
-
- {{ coveragePercentage }}% - Coverage -
-
-
-
-
-
- -
-
- test.cpp -
-
-
- {{ index + 1 }} - {{ line }} -
-
-
-
-
-
-
-
- - - - \ No newline at end of file diff --git a/web/server/vue-cli/src/services/api/cc.service.js b/web/server/vue-cli/src/services/api/cc.service.js index 354d08dd16..170e377b69 100644 --- a/web/server/vue-cli/src/services/api/cc.service.js +++ b/web/server/vue-cli/src/services/api/cc.service.js @@ -100,6 +100,47 @@ class CodeCheckerService extends BaseService { })); }); } + + /** + * Get code coverage data for a specific file + * @param {number} fileId - The ID of the file to get coverage for + * @param {Array} runIds - Array of run IDs to get coverage from + * @returns {Promise} - Returns promise that resolves to coverage data + * @example + * { + * fileId: 123, + * filePath: "/path/to/file.cpp", + * totalLines: 100, + * coveredLines: 80, + * uncoveredLines: 20, + * coveragePercentage: 80, + * lineCoverage: [ + * { + * lineNumber: 1, + * covered: true, + * executionCount: 5, + * lastExecution: "2024-04-20T10:00:00Z" + * }, + * { + * lineNumber: 2, + * covered: false, + * executionCount: 0, + * lastExecution: null + * } + * ] + * } + */ + getCodeCoverage(fileId, runIds) { + return new Promise(resolve => { + this.getClient().getCodeCoverage( + fileId, + runIds, + handleThriftError(coverageData => { + resolve(coverageData); + }) + ); + }); + } } const ccService = new CodeCheckerService(); diff --git a/web/server/vue-cli/src/views/ReportDetail.vue b/web/server/vue-cli/src/views/ReportDetail.vue index f8b8d06891..ec19d29747 100644 --- a/web/server/vue-cli/src/views/ReportDetail.vue +++ b/web/server/vue-cli/src/views/ReportDetail.vue @@ -216,7 +216,128 @@ export default { } }, - onReportTreeClick(item) { + async loadCoverageData(fileId) { + try { + console.log("Loading coverage data for fileId:", fileId); + // Mock data with partially covered lines + const coverageData = { + fileId: fileId, + filePath: "example.c", + totalLines: 14, + coveredLines: 8, + uncoveredLines: 4, + coveragePercentage: 66.67, + lineCoverage: [ + { + lineNumber: 1, + covered: true, + partiallyCovered: false, + executionCount: 5, + lastExecution: new Date().toISOString() + }, + { + lineNumber: 2, + covered: true, + partiallyCovered: false, + executionCount: 3, + lastExecution: new Date().toISOString() + }, + { + lineNumber: 3, + covered: true, + partiallyCovered: true, // Partially covered + executionCount: 2, + lastExecution: new Date().toISOString() + }, + { + lineNumber: 4, + covered: false, + partiallyCovered: false, + executionCount: 0, + lastExecution: null + }, + { + lineNumber: 5, + covered: true, + partiallyCovered: false, + executionCount: 4, + lastExecution: new Date().toISOString() + }, + { + lineNumber: 6, + covered: true, + partiallyCovered: true, // Partially covered + executionCount: 1, + lastExecution: new Date().toISOString() + }, + { + lineNumber: 7, + covered: true, + partiallyCovered: false, + executionCount: 6, + lastExecution: new Date().toISOString() + }, + { + lineNumber: 8, + covered: false, + partiallyCovered: false, + executionCount: 0, + lastExecution: null + }, + { + lineNumber: 9, + covered: true, + partiallyCovered: true, // Partially covered + executionCount: 1, + lastExecution: new Date().toISOString() + }, + { + lineNumber: 10, + covered: true, + partiallyCovered: false, + executionCount: 2, + lastExecution: new Date().toISOString() + }, + { + lineNumber: 11, + covered: false, + partiallyCovered: false, + executionCount: 0, + lastExecution: null + }, + { + lineNumber: 12, + covered: true, + partiallyCovered: false, + executionCount: 3, + lastExecution: new Date().toISOString() + }, + { + lineNumber: 13, + covered: true, + partiallyCovered: true, // Partially covered + executionCount: 1, + lastExecution: new Date().toISOString() + }, + { + lineNumber: 14, + covered: false, + partiallyCovered: false, + executionCount: 0, + lastExecution: null + } + ] + }; + + console.log("Coverage data loaded:", coverageData); + return coverageData; + } catch (err) { + console.error("Failed to load coverage data:", err); + return null; + } + }, + + async onReportTreeClick(item) { if (!item) return; if (item.report) { @@ -224,6 +345,12 @@ export default { this.updateUrl(); } + // Load coverage data when a new file is selected + if (item.report && item.report.fileId) { + const coverageData = await this.loadCoverageData(item.report.fileId); + item.coverageData = coverageData; + } + this.treeItem = item; }, From b424ac1a9acfd4c90e6e697f790e8363c11cbdf7 Mon Sep 17 00:00:00 2001 From: Mykhailo Shepelenko Date: Sat, 26 Apr 2025 18:57:26 +0200 Subject: [PATCH 03/10] small modifications --- .../vue-cli/src/components/Report/Report.vue | 141 ++++------- .../vue-cli/src/services/api/cc.service.js | 6 +- web/server/vue-cli/src/views/ReportDetail.vue | 232 ++++++++---------- 3 files changed, 157 insertions(+), 222 deletions(-) diff --git a/web/server/vue-cli/src/components/Report/Report.vue b/web/server/vue-cli/src/components/Report/Report.vue index 085f7fc37e..453b7dd881 100644 --- a/web/server/vue-cli/src/components/Report/Report.vue +++ b/web/server/vue-cli/src/components/Report/Report.vue @@ -161,7 +161,7 @@
@@ -172,10 +172,17 @@ dense :hide-details="true" :loading="coverageLoading" + @change="val => { + if (val) { + loadCoverageData(); + } else { + clearCoverageHighlighting(); + } + }" /> ({{ Math.round(coverageData.coveragePercentage) }}% covered) @@ -373,12 +380,13 @@ export default { SetCleanupPlanBtn, ShowReportInfoDialog, ToggleBlameViewBtn, - UserIcon + UserIcon, }, directives: { FillHeight }, mixins: [ GitBlameMixin ], props: { - treeItem: { type: Object, default: null } + treeItem: { type: Object, default: null }, + coverageData: { type: Object, default: null } }, emits: [ "update-review-data" ], @@ -408,7 +416,6 @@ export default { reportId: null, enableBlameView, docUrl: null, - coverageData: null, showCoverage: false, coverageLoading: false, }; @@ -505,6 +512,19 @@ export default { this.clearCoverageHighlighting(); } } + }, + + coverageData: { + handler(newVal) { + console.log("Coverage data changed:", newVal); + if (newVal) { + this.coverageData = newVal; + if (this.showCoverage) { + this.updateCoverageHighlighting(); + } + } + }, + immediate: true } }, @@ -1000,100 +1020,22 @@ export default { this.coverageLoading = true; try { - // Mock data for testing - this.coverageData = { - fileId: this.report.fileId, - filePath: "example.c", - totalLines: 12, - coveredLines: 8, - uncoveredLines: 4, - coveragePercentage: 66.67, - lineCoverage: [ - { - lineNumber: 1, - covered: true, - executionCount: 1, - lastExecution: "2024-04-20T10:00:00Z" - }, - { - lineNumber: 2, - covered: true, - executionCount: 1, - lastExecution: "2024-04-20T10:00:00Z" - }, - { - lineNumber: 3, - covered: true, - executionCount: 1, - lastExecution: "2024-04-20T10:00:00Z" - }, - { - lineNumber: 4, - covered: false, - executionCount: 0, - lastExecution: null - }, // Uncovered - division by zero - { - lineNumber: 5, - covered: true, - executionCount: 1, - lastExecution: "2024-04-20T10:00:00Z" - }, - { - lineNumber: 6, - covered: true, - executionCount: 1, - lastExecution: "2024-04-20T10:00:00Z" - }, - { - lineNumber: 7, - covered: true, - executionCount: 1, - lastExecution: "2024-04-20T10:00:00Z" - }, - { - lineNumber: 8, - covered: false, - executionCount: 0, - lastExecution: null - }, // Uncovered - division by zero - { - lineNumber: 9, - covered: false, - executionCount: 0, - lastExecution: null - }, // Uncovered - division by zero - { - lineNumber: 10, - covered: true, - executionCount: 1, - lastExecution: "2024-04-20T10:00:00Z" - }, - { - lineNumber: 11, - covered: true, - executionCount: 1, - lastExecution: "2024-04-20T10:00:00Z" - }, - { - lineNumber: 12, - covered: true, - executionCount: 1, - lastExecution: "2024-04-20T10:00:00Z" - } - ] - }; - - // Original backend call commented out for testing - /* + // Use the coverage data from props if available + if (this.coverageData) { + console.log("Using coverage data from props:", this.coverageData); + this.updateCoverageHighlighting(); + return; + } + + // Fallback to loading coverage data if not provided via props const runIds = this.report.runId ? [ this.report.runId ] : []; this.coverageData = await ccService.getCodeCoverage( this.report.fileId, runIds ); - */ this.updateCoverageHighlighting(); + console.log("Loaded coverageData:", this.coverageData); } catch (err) { console.error("Failed to load coverage data:", err); } finally { @@ -1262,9 +1204,13 @@ export default { } } + .show-coverage { + margin-right: 8px; + } + .coverage-percentage { font-size: 0.9em; - color: var(--v-grey-darken3); + white-space: nowrap; } .editor { @@ -1318,4 +1264,13 @@ export default { ::v-deep .CodeMirror-linenumber { color: #666; } + +::v-deep .v-input--checkbox.show-coverage { + margin-top: 0; + margin-bottom: 0; + + .v-input__slot { + margin-bottom: 0; + } +} diff --git a/web/server/vue-cli/src/services/api/cc.service.js b/web/server/vue-cli/src/services/api/cc.service.js index 170e377b69..225ca7fc64 100644 --- a/web/server/vue-cli/src/services/api/cc.service.js +++ b/web/server/vue-cli/src/services/api/cc.service.js @@ -118,14 +118,12 @@ class CodeCheckerService extends BaseService { * { * lineNumber: 1, * covered: true, - * executionCount: 5, - * lastExecution: "2024-04-20T10:00:00Z" + * partiallyCovered: false * }, * { * lineNumber: 2, * covered: false, - * executionCount: 0, - * lastExecution: null + * partiallyCovered: false * } * ] * } diff --git a/web/server/vue-cli/src/views/ReportDetail.vue b/web/server/vue-cli/src/views/ReportDetail.vue index ec19d29747..421b62cab8 100644 --- a/web/server/vue-cli/src/views/ReportDetail.vue +++ b/web/server/vue-cli/src/views/ReportDetail.vue @@ -88,6 +88,7 @@ Date: Wed, 30 Apr 2025 17:21:18 +0200 Subject: [PATCH 04/10] code coverage visualisation added --- web/server/codechecker_server/cli/server.py | 9 +-- web/server/codechecker_server/server.py | 47 ++++++++--- web/server/vue-cli/config/webpack.dev.js | 4 +- web/server/vue-cli/nightwatch.conf.js | 2 +- .../vue-cli/src/components/Report/Report.vue | 56 ++++++++----- .../vue-cli/src/services/api/cc.service.js | 22 ++--- web/server/vue-cli/src/views/ReportDetail.vue | 81 +++++++------------ 7 files changed, 116 insertions(+), 105 deletions(-) diff --git a/web/server/codechecker_server/cli/server.py b/web/server/codechecker_server/cli/server.py index dd91a7f841..164c6441e2 100644 --- a/web/server/codechecker_server/cli/server.py +++ b/web/server/codechecker_server/cli/server.py @@ -33,7 +33,7 @@ from codechecker_report_converter import twodim from codechecker_common import arg, cmd_config, logger, util -from codechecker_common.compatibility.multiprocessing import cpu_count +from codechecker_common.compatibility.multiprocessing import Pool, cpu_count from codechecker_server import instance_manager, server from codechecker_server.database import database @@ -47,8 +47,6 @@ from codechecker_web.shared import webserver_context, database_status, \ host_check, env -import concurrent.futures - LOG = logger.get_logger('server') @@ -717,10 +715,9 @@ def _get_migration_decisions() -> List[Tuple[str, str, bool]]: failed_products: List[Tuple[str, DBStatus]] = [] thr_count = util.clamp(1, len(scheduled_upgrades_or_inits), cpu_count()) - with concurrent.futures.ProcessPoolExecutor(max_workers=thr_count) as executor: + with Pool(max_workers=thr_count) as executor: LOG.info("Initialising/upgrading products using %d concurrent " "jobs...", thr_count) - futures = [] for product_cfg, return_status in \ zip(scheduled_upgrades_or_inits, executor.map( # Bind the first 2 non-changing arguments of @@ -1135,4 +1132,4 @@ def main(args): except FileNotFoundError as fnerr: LOG.error(fnerr) sys.exit(1) - server_init_start(args) + server_init_start(args) \ No newline at end of file diff --git a/web/server/codechecker_server/server.py b/web/server/codechecker_server/server.py index fb6647bfbf..7bf253f7bc 100644 --- a/web/server/codechecker_server/server.py +++ b/web/server/codechecker_server/server.py @@ -67,8 +67,6 @@ from .database.database import DBSession from .database.run_db_model import IDENTIFIER as RUN_META, Run, RunLock -import concurrent.futures - LOG = get_logger('server') @@ -701,7 +699,32 @@ def cleanup_run_db(self): return True -def _do_db_cleanup(config_database, context, check_env) \ +def _do_db_cleanup(context, check_env, + id_: int, endpoint: str, display_name: str, + connection_str: str) -> Tuple[Optional[bool], str]: + # This functions is a concurrent job handler! + try: + prod = Product(id_, endpoint, display_name, connection_str, + context, check_env) + prod.connect(init_db=False) + if prod.db_status != DBStatus.OK: + status_str = database_status.db_status_msg.get(prod.db_status) + return None, \ + f"Cleanup not attempted, database status is \"{status_str}\"" + + prod.cleanup_run_db() + prod.teardown() + + # Result is hard-wired to True, because the db_cleanup routines + # swallow and log the potential errors but do not return them. + return True, "" + except Exception as e: + import traceback + traceback.print_exc() + return False, str(e) + + +def _do_db_cleanups(config_database, context, check_env) \ -> Tuple[bool, List[Tuple[str, str]]]: """ Performs on-demand start-up database cleanup on all the products present @@ -729,16 +752,14 @@ def _get_products() -> List[Product]: thr_count = util.clamp(1, len(products), cpu_count()) overall_result, failures = True, [] - with concurrent.futures.ProcessPoolExecutor(max_workers=thr_count) as executor: + with Pool(max_workers=thr_count) as executor: LOG.info("Performing database cleanup using %d concurrent jobs...", thr_count) - futures = [] - for product in products: - futures.append(executor.submit( - partial(_do_db_cleanup, context, check_env), - *product)) - for future in concurrent.futures.as_completed(futures): - success, reason = future.result() + for product, result in \ + zip(products, executor.map( + partial(_do_db_cleanup, context, check_env), + *zip(*products))): + success, reason = result if not success: _, endpoint, _, _ = product overall_result = False @@ -1127,7 +1148,7 @@ def check_callback_url_format(provider_name: str, callback_url: str): sys.exit(1) if not skip_db_cleanup: - all_success, fails = _do_db_cleanup(config_sql_server, + all_success, fails = _do_db_cleanups(config_sql_server, context, check_env) if not all_success: @@ -1254,4 +1275,4 @@ def add_initial_run_database(config_sql_server, product_connection): sess.commit() sess.close() - LOG.debug("Default product set up.") + LOG.debug("Default product set up.") \ No newline at end of file diff --git a/web/server/vue-cli/config/webpack.dev.js b/web/server/vue-cli/config/webpack.dev.js index c585505fa3..dd8fc4be69 100644 --- a/web/server/vue-cli/config/webpack.dev.js +++ b/web/server/vue-cli/config/webpack.dev.js @@ -14,7 +14,7 @@ const CC_SERVICE_ENDPOINTS = [ // Location of the Thrift API server. const CC_THRIFT_API_HOST = process.env.CC_THRIFT_API_HOST || 'http://localhost'; -const CC_THRIFT_API_PORT = process.env.CC_THRIFT_API_PORT || 8001; +const CC_THRIFT_API_PORT = process.env.CC_THRIFT_API_PORT || 8002; module.exports = merge(common, { mode: 'development', @@ -24,7 +24,7 @@ module.exports = merge(common, { }, devtool: 'inline-source-map', devServer: { - port: 8081, + port: 8080, hot: true, historyApiFallback: { // If the URL contains a product endpoint and we server a static file diff --git a/web/server/vue-cli/nightwatch.conf.js b/web/server/vue-cli/nightwatch.conf.js index 3013b3724e..dd810cabd4 100644 --- a/web/server/vue-cli/nightwatch.conf.js +++ b/web/server/vue-cli/nightwatch.conf.js @@ -1,5 +1,5 @@ const host = process.env.HOST || "localhost"; -const port = process.env.PORT || 8001; +const port = process.env.PORT || 8002; const chromeHeadless = process.env.CHROME_HEADLESS; module.exports = { diff --git a/web/server/vue-cli/src/components/Report/Report.vue b/web/server/vue-cli/src/components/Report/Report.vue index 453b7dd881..7fbadc003a 100644 --- a/web/server/vue-cli/src/components/Report/Report.vue +++ b/web/server/vue-cli/src/components/Report/Report.vue @@ -1050,29 +1050,32 @@ export default { console.log("Total lines in editor:", this.editor.lineCount()); console.log("Coverage data:", this.coverageData); - // Create a map for faster line lookup - const coverageMap = new Map( - this.coverageData.lineCoverage.map(line => [ line.lineNumber, line ]) - ); - this.editor.operation(() => { for (let i = 0; i < this.editor.lineCount(); i++) { - const lineData = coverageMap.get(i + 1); + const lineNumber = i + 1; + const lineData = this.coverageData.lineCoverage.find(line => { + const { start, end } = line.lineRange; + return start <= lineNumber && end >= lineNumber; + }); + if (lineData) { let className; - if (lineData.partiallyCovered) { + switch (lineData.coverageStatus) { + case "covered": + className = "coverage-covered-line"; + break; + case "uncovered": + className = "coverage-uncovered-line"; + break; + case "partially-covered": className = "coverage-partial-line"; - } else { - className = lineData.covered - ? "coverage-covered-line" - : "coverage-uncovered-line"; + break; + default: + className = ""; } - console.log(`Line ${i + 1}:`, { - covered: lineData.covered, - partiallyCovered: lineData.partiallyCovered, - executionCount: lineData.executionCount, - lastExecution: lineData.lastExecution, + console.log(`Line ${lineNumber}:`, { + coverageStatus: lineData.coverageStatus, className }); @@ -1080,7 +1083,7 @@ export default { this.editor.addLineClass(i, type, className); }); } else { - console.log(`Line ${i + 1}: No coverage data available`); + console.log(`Line ${lineNumber}: No coverage data available`); } } }); @@ -1105,12 +1108,23 @@ export default { getLineCoverageClass(lineNumber) { if (!this.coverageData || !this.showCoverage) return ""; - const lineInfo = this.coverageData.lineCoverage.find( - line => line.lineNumber === lineNumber - ); + const lineInfo = this.coverageData.lineCoverage.find(line => { + const { start, end } = line.lineRange; + return start <= lineNumber && end >= lineNumber; + }); if (!lineInfo) return "coverage-unknown"; - return lineInfo.covered ? "coverage-covered" : "coverage-uncovered"; + + switch (lineInfo.coverageStatus) { + case "covered": + return "coverage-covered"; + case "uncovered": + return "coverage-uncovered"; + case "partially-covered": + return "coverage-partial"; + default: + return "coverage-unknown"; + } }, } }; diff --git a/web/server/vue-cli/src/services/api/cc.service.js b/web/server/vue-cli/src/services/api/cc.service.js index 225ca7fc64..c32d444474 100644 --- a/web/server/vue-cli/src/services/api/cc.service.js +++ b/web/server/vue-cli/src/services/api/cc.service.js @@ -115,16 +115,18 @@ class CodeCheckerService extends BaseService { * uncoveredLines: 20, * coveragePercentage: 80, * lineCoverage: [ - * { - * lineNumber: 1, - * covered: true, - * partiallyCovered: false - * }, - * { - * lineNumber: 2, - * covered: false, - * partiallyCovered: false - * } + * { + * lineRange: { start: 1, end: 2 }, + * coverageStatus: "uncovered", + * }, + * { + * lineRange: { start: 3, end: 4 }, + * coverageStatus: "covered", + * }, + * { + * lineRange: { start: 5, end: 6 }, + * coverageStatus: "partially-covered", + * } * ] * } */ diff --git a/web/server/vue-cli/src/views/ReportDetail.vue b/web/server/vue-cli/src/views/ReportDetail.vue index 421b62cab8..768c1032ff 100644 --- a/web/server/vue-cli/src/views/ReportDetail.vue +++ b/web/server/vue-cli/src/views/ReportDetail.vue @@ -245,87 +245,64 @@ export default { coveragePercentage: 57.1, lineCoverage: [ { - lineNumber: 1, - covered: true, - partiallyCovered: false, - executionCount: 5 + lineRange: { start: 1, end: 1 }, + coverageStatus: "covered" }, { - lineNumber: 2, - covered: false, - partiallyCovered: false + lineRange: { start: 2, end: 2 }, + coverageStatus: "uncovered" }, { - lineNumber: 3, - covered: true, - partiallyCovered: false, - executionCount: 10 + lineRange: { start: 3, end: 3 }, + coverageStatus: "covered" }, { - lineNumber: 4, - covered: true, - partiallyCovered: true, - executionCount: 3 + lineRange: { start: 4, end: 4 }, + coverageStatus: "partially-covered" }, { - lineNumber: 5, - covered: false, - partiallyCovered: false + lineRange: { start: 5, end: 5 }, + coverageStatus: "uncovered" }, { - lineNumber: 6, - covered: true, - partiallyCovered: false, - executionCount: 2 + lineRange: { start: 6, end: 6 }, + coverageStatus: "covered" }, { - lineNumber: 7, - covered: true, - partiallyCovered: true, - executionCount: 8 + lineRange: { start: 7, end: 7 }, + coverageStatus: "partially-covered" }, { - lineNumber: 8, - covered: false, - partiallyCovered: false + lineRange: { start: 8, end: 8 }, + coverageStatus: "uncovered" }, { - lineNumber: 9, - covered: true, - partiallyCovered: false, - executionCount: 4 + lineRange: { start: 9, end: 9 }, + coverageStatus: "covered" }, { - lineNumber: 10, - covered: true, - partiallyCovered: true, - executionCount: 6 + lineRange: { start: 10, end: 10 }, + coverageStatus: "partially-covered" }, { - lineNumber: 11, - covered: false, - partiallyCovered: false + lineRange: { start: 11, end: 11 }, + coverageStatus: "uncovered" }, { - lineNumber: 12, - covered: true, - partiallyCovered: false, - executionCount: 7 + lineRange: { start: 12, end: 12 }, + coverageStatus: "covered" }, { - lineNumber: 13, - covered: true, - partiallyCovered: false, - executionCount: 9 + lineRange: { start: 13, end: 13 }, + coverageStatus: "covered" }, { - lineNumber: 14, - covered: false, - partiallyCovered: false + lineRange: { start: 14, end: 14 }, + coverageStatus: "uncovered" } ] }; - + console.log("Using mock coverage data:", mockCoverageData); item.coverageData = mockCoverageData; } catch (err) { From 985e2ab6584cbbc01fcbfd1ec6db079608d133d1 Mon Sep 17 00:00:00 2001 From: Mykhailo Shepelenko Date: Wed, 30 Apr 2025 17:31:13 +0200 Subject: [PATCH 05/10] mock data is comented out --- web/server/vue-cli/src/views/ReportDetail.vue | 136 +++++++++--------- 1 file changed, 68 insertions(+), 68 deletions(-) diff --git a/web/server/vue-cli/src/views/ReportDetail.vue b/web/server/vue-cli/src/views/ReportDetail.vue index 768c1032ff..97cd6c8c45 100644 --- a/web/server/vue-cli/src/views/ReportDetail.vue +++ b/web/server/vue-cli/src/views/ReportDetail.vue @@ -236,75 +236,75 @@ export default { // item.coverageData = coverageData; // Mock data for testing - const mockCoverageData = { - fileId: item.report.fileId, - filePath: item.report.checkedFile, - totalLines: 14, - coveredLines: 8, - uncoveredLines: 6, - coveragePercentage: 57.1, - lineCoverage: [ - { - lineRange: { start: 1, end: 1 }, - coverageStatus: "covered" - }, - { - lineRange: { start: 2, end: 2 }, - coverageStatus: "uncovered" - }, - { - lineRange: { start: 3, end: 3 }, - coverageStatus: "covered" - }, - { - lineRange: { start: 4, end: 4 }, - coverageStatus: "partially-covered" - }, - { - lineRange: { start: 5, end: 5 }, - coverageStatus: "uncovered" - }, - { - lineRange: { start: 6, end: 6 }, - coverageStatus: "covered" - }, - { - lineRange: { start: 7, end: 7 }, - coverageStatus: "partially-covered" - }, - { - lineRange: { start: 8, end: 8 }, - coverageStatus: "uncovered" - }, - { - lineRange: { start: 9, end: 9 }, - coverageStatus: "covered" - }, - { - lineRange: { start: 10, end: 10 }, - coverageStatus: "partially-covered" - }, - { - lineRange: { start: 11, end: 11 }, - coverageStatus: "uncovered" - }, - { - lineRange: { start: 12, end: 12 }, - coverageStatus: "covered" - }, - { - lineRange: { start: 13, end: 13 }, - coverageStatus: "covered" - }, - { - lineRange: { start: 14, end: 14 }, - coverageStatus: "uncovered" - } - ] - }; + // const mockCoverageData = { + // fileId: item.report.fileId, + // filePath: item.report.checkedFile, + // totalLines: 14, + // coveredLines: 8, + // uncoveredLines: 6, + // coveragePercentage: 57.1, + // lineCoverage: [ + // { + // lineRange: { start: 1, end: 1 }, + // coverageStatus: "covered" + // }, + // { + // lineRange: { start: 2, end: 2 }, + // coverageStatus: "uncovered" + // }, + // { + // lineRange: { start: 3, end: 3 }, + // coverageStatus: "covered" + // }, + // { + // lineRange: { start: 4, end: 4 }, + // coverageStatus: "partially-covered" + // }, + // { + // lineRange: { start: 5, end: 5 }, + // coverageStatus: "uncovered" + // }, + // { + // lineRange: { start: 6, end: 6 }, + // coverageStatus: "covered" + // }, + // { + // lineRange: { start: 7, end: 7 }, + // coverageStatus: "partially-covered" + // }, + // { + // lineRange: { start: 8, end: 8 }, + // coverageStatus: "uncovered" + // }, + // { + // lineRange: { start: 9, end: 9 }, + // coverageStatus: "covered" + // }, + // { + // lineRange: { start: 10, end: 10 }, + // coverageStatus: "partially-covered" + // }, + // { + // lineRange: { start: 11, end: 11 }, + // coverageStatus: "uncovered" + // }, + // { + // lineRange: { start: 12, end: 12 }, + // coverageStatus: "covered" + // }, + // { + // lineRange: { start: 13, end: 13 }, + // coverageStatus: "covered" + // }, + // { + // lineRange: { start: 14, end: 14 }, + // coverageStatus: "uncovered" + // } + // ] + // }; - console.log("Using mock coverage data:", mockCoverageData); - item.coverageData = mockCoverageData; + // console.log("Using mock coverage data:", mockCoverageData); + // item.coverageData = mockCoverageData; } catch (err) { console.error("Failed to load coverage data:", err); } From 01b245b9f3ce09c1836d625eec733584849a6cca Mon Sep 17 00:00:00 2001 From: Mykhailo Shepelenko Date: Wed, 30 Apr 2025 19:37:07 +0200 Subject: [PATCH 06/10] console logs are removed --- .../vue-cli/src/components/Report/Report.vue | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/web/server/vue-cli/src/components/Report/Report.vue b/web/server/vue-cli/src/components/Report/Report.vue index 7fbadc003a..83a6cc55e8 100644 --- a/web/server/vue-cli/src/components/Report/Report.vue +++ b/web/server/vue-cli/src/components/Report/Report.vue @@ -470,7 +470,6 @@ export default { if (!newVal) return; if (newVal.coverageData) { - console.log("New coverage data received:", newVal.coverageData); this.coverageData = newVal.coverageData; if (this.showCoverage) { this.updateCoverageHighlighting(); @@ -516,7 +515,6 @@ export default { coverageData: { handler(newVal) { - console.log("Coverage data changed:", newVal); if (newVal) { this.coverageData = newVal; if (this.showCoverage) { @@ -952,7 +950,6 @@ export default { .filter(textMarker => { let line = null; - // If not in viewport. try { line = textMarker.lines[0].lineNo(); } catch (ex) { @@ -1022,7 +1019,6 @@ export default { try { // Use the coverage data from props if available if (this.coverageData) { - console.log("Using coverage data from props:", this.coverageData); this.updateCoverageHighlighting(); return; } @@ -1035,9 +1031,8 @@ export default { ); this.updateCoverageHighlighting(); - console.log("Loaded coverageData:", this.coverageData); } catch (err) { - console.error("Failed to load coverage data:", err); + // Error handling without console.log } finally { this.coverageLoading = false; } @@ -1046,10 +1041,6 @@ export default { updateCoverageHighlighting() { if (!this.coverageData || !this.showCoverage) return; - console.group("Coverage Highlighting Update"); - console.log("Total lines in editor:", this.editor.lineCount()); - console.log("Coverage data:", this.coverageData); - this.editor.operation(() => { for (let i = 0; i < this.editor.lineCount(); i++) { const lineNumber = i + 1; @@ -1073,22 +1064,13 @@ export default { default: className = ""; } - - console.log(`Line ${lineNumber}:`, { - coverageStatus: lineData.coverageStatus, - className - }); [ "wrap", "background", "gutter", "line" ].forEach(type => { this.editor.addLineClass(i, type, className); }); - } else { - console.log(`Line ${lineNumber}: No coverage data available`); } } }); - - console.groupEnd(); }, clearCoverageHighlighting() { From 5de43702a8b2419b6c35c60837bd11224386e0fc Mon Sep 17 00:00:00 2001 From: Mykhailo Shepelenko Date: Wed, 30 Apr 2025 19:42:32 +0200 Subject: [PATCH 07/10] empty lines added --- web/server/codechecker_server/cli/server.py | 3 ++- web/server/codechecker_server/server.py | 30 +-------------------- 2 files changed, 3 insertions(+), 30 deletions(-) diff --git a/web/server/codechecker_server/cli/server.py b/web/server/codechecker_server/cli/server.py index 164c6441e2..0d494cf72c 100644 --- a/web/server/codechecker_server/cli/server.py +++ b/web/server/codechecker_server/cli/server.py @@ -1132,4 +1132,5 @@ def main(args): except FileNotFoundError as fnerr: LOG.error(fnerr) sys.exit(1) - server_init_start(args) \ No newline at end of file + server_init_start(args) + \ No newline at end of file diff --git a/web/server/codechecker_server/server.py b/web/server/codechecker_server/server.py index 7bf253f7bc..477d0aedb4 100644 --- a/web/server/codechecker_server/server.py +++ b/web/server/codechecker_server/server.py @@ -1247,32 +1247,4 @@ def unregister_handler(pid): http_server.serve_forever() LOG.info("Webserver quit.") - - -def add_initial_run_database(config_sql_server, product_connection): - """ - Create a default run database as SQLite in the config directory, - and add it to the list of products in the config database specified by - db_conn_string. - """ - - # Connect to the configuration database - LOG.debug("Creating database engine for CONFIG DATABASE...") - __engine = config_sql_server.create_engine() - product_session = sessionmaker(bind=__engine) - - # Load the initial list of products and create the connections. - sess = product_session() - products = sess.query(ORMProduct).all() - if products: - raise ValueError("Called create_initial_run_database on non-empty " - "config database -- you shouldn't have done this!") - - LOG.debug("Adding default product to the config db...") - product = ORMProduct('Default', product_connection, 'Default', - "Default product created at server start.") - sess.add(product) - sess.commit() - sess.close() - - LOG.debug("Default product set up.") \ No newline at end of file + \ No newline at end of file From 44a3d706ba2cebd83d5620ef6bcb226bca938f36 Mon Sep 17 00:00:00 2001 From: Mykhailo Shepelenko Date: Wed, 30 Apr 2025 19:43:13 +0200 Subject: [PATCH 08/10] empty lines added --- web/server/codechecker_server/server.py | 29 +++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/web/server/codechecker_server/server.py b/web/server/codechecker_server/server.py index 477d0aedb4..4668af5ba2 100644 --- a/web/server/codechecker_server/server.py +++ b/web/server/codechecker_server/server.py @@ -1247,4 +1247,33 @@ def unregister_handler(pid): http_server.serve_forever() LOG.info("Webserver quit.") + + +def add_initial_run_database(config_sql_server, product_connection): + """ + Create a default run database as SQLite in the config directory, + and add it to the list of products in the config database specified by + db_conn_string. + """ + + # Connect to the configuration database + LOG.debug("Creating database engine for CONFIG DATABASE...") + __engine = config_sql_server.create_engine() + product_session = sessionmaker(bind=__engine) + + # Load the initial list of products and create the connections. + sess = product_session() + products = sess.query(ORMProduct).all() + if products: + raise ValueError("Called create_initial_run_database on non-empty " + "config database -- you shouldn't have done this!") + + LOG.debug("Adding default product to the config db...") + product = ORMProduct('Default', product_connection, 'Default', + "Default product created at server start.") + sess.add(product) + sess.commit() + sess.close() + + LOG.debug("Default product set up.") \ No newline at end of file From 48f6a3ef4836d3ebe80a5253fe0072fac9c50795 Mon Sep 17 00:00:00 2001 From: Mykhailo Shepelenko Date: Wed, 30 Apr 2025 19:46:28 +0200 Subject: [PATCH 09/10] empty lines added --- web/server/codechecker_server/cli/server.py | 3 ++- web/server/codechecker_server/server.py | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/web/server/codechecker_server/cli/server.py b/web/server/codechecker_server/cli/server.py index 0d494cf72c..86ec87e450 100644 --- a/web/server/codechecker_server/cli/server.py +++ b/web/server/codechecker_server/cli/server.py @@ -1133,4 +1133,5 @@ def main(args): LOG.error(fnerr) sys.exit(1) server_init_start(args) - \ No newline at end of file + + diff --git a/web/server/codechecker_server/server.py b/web/server/codechecker_server/server.py index 4668af5ba2..1147c30421 100644 --- a/web/server/codechecker_server/server.py +++ b/web/server/codechecker_server/server.py @@ -1276,4 +1276,4 @@ def add_initial_run_database(config_sql_server, product_connection): sess.close() LOG.debug("Default product set up.") - \ No newline at end of file + From 766b1f8171a0dc9cf3e288483c6c7f086bb7482d Mon Sep 17 00:00:00 2001 From: Mykhailo Shepelenko Date: Wed, 30 Apr 2025 19:52:43 +0200 Subject: [PATCH 10/10] console logs are removed --- web/server/vue-cli/src/views/ReportDetail.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/web/server/vue-cli/src/views/ReportDetail.vue b/web/server/vue-cli/src/views/ReportDetail.vue index 97cd6c8c45..82aafd5e94 100644 --- a/web/server/vue-cli/src/views/ReportDetail.vue +++ b/web/server/vue-cli/src/views/ReportDetail.vue @@ -306,7 +306,7 @@ export default { // console.log("Using mock coverage data:", mockCoverageData); // item.coverageData = mockCoverageData; } catch (err) { - console.error("Failed to load coverage data:", err); + //console.error("Failed to load coverage data:", err); } }