From 4ed796ef9f70941531137e3b52e7bef406ba649f Mon Sep 17 00:00:00 2001 From: Arnaud Ferraris Date: Thu, 21 Aug 2025 14:10:13 +0200 Subject: [PATCH 1/3] src: send_kcidb: process coverage-report nodes Coverage report nodes are meant to provide global coverage percentages for a while `kbuild`, aggregating the results from individual test jobs. As such, they shouldn't be sent to KCIDB as nodes, but rather parsed in order to submit an updated build node which includes the functions/lines coverage percentages as misc fields. Add a new processing function for such nodes, which are of kind `process` and are named `coverage-report-`. Signed-off-by: Arnaud Ferraris --- src/send_kcidb.py | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/src/send_kcidb.py b/src/send_kcidb.py index a58839707..8b8898b2a 100755 --- a/src/send_kcidb.py +++ b/src/send_kcidb.py @@ -566,6 +566,38 @@ def _parse_test_node(self, origin, test_node): return parsed_test_node, dummy_build + def _parse_coverage_node(self, origin, node): + # Coverage report nodes are not sent to the DB themselves, but rather + # parsed to submit an updated build node including the functions/lines + # coverage percentages as misc fields. + build_node = self._get_node_cached(node['parent']) + # Coverage report nodes are also created for individual test jobs, the + # results of which being only useful to the "top-level" (i.e. direct + # child of the kbuild node) instance. + if build_node['kind'] != 'kbuild': + self.log.debug(f"{node['id']} is not a top-level coverage node, skipping") + return [] + parsed_coverage_node = self._parse_build_node(origin, build_node) + + # Add misc fields for coverage results + child_nodes = self._api.node.find({'parent': node['id'], 'kind': 'test'}) + for child in child_nodes: + if not child['name'].startswith('coverage.'): + continue + if 'misc' not in child['data'] or 'measurement' not in child['data']['misc']: + self.log.warning(f"No measurement in node '{child['name']}' ({child['id']})") + continue + field_name = self._replace_restricted_chars(child['name'], r'^[a-zA-Z0-9_]*$') + parsed_coverage_node['misc'][field] = child['data']['misc']['measurement'] + + # Add HTML coverage report to the build node's artifacts + artifacts = node.get('artifacts') + if artifacts: + parsed_coverage_node['misc']['coverage_report_url'] = artifacts.get('coverage_report') + parsed_coverage_node['misc']['coverage_log_url'] = artifacts.get('log') + + return [parsed_coverage_node] + def _get_test_data(self, node, origin, parsed_test_node, parsed_build_node): test_node, build_node = self._parse_test_node( @@ -809,6 +841,9 @@ def _process_node(self, node, origin, is_hierarchy): elif node['kind'] == 'kbuild': parsed_data['build_node'] = self._parse_build_node(origin, node) + elif node['kind'] == 'process' and node['name'].startswith('coverage-report'): + parsed_data['build_node'] = self._parse_coverage_node(origin, node) + elif node['kind'] in ['test', 'job']: self._get_test_data(node, origin, parsed_data['test_node'], parsed_data['build_node']) From bb9efa40047e6b8c0f60bcd64ffa237c7bf410ba Mon Sep 17 00:00:00 2001 From: Jeny Sadadia Date: Sat, 23 Aug 2025 14:16:32 +0530 Subject: [PATCH 2/3] src/send_kcidb: fixes of the previous commit Fix `_parse_coverage_node` method: 1. Fix `field_name` 2. Fix extraction of parsed build node Signed-off-by: Jeny Sadadia --- src/send_kcidb.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/send_kcidb.py b/src/send_kcidb.py index 8b8898b2a..594a8c6b9 100755 --- a/src/send_kcidb.py +++ b/src/send_kcidb.py @@ -577,7 +577,8 @@ def _parse_coverage_node(self, origin, node): if build_node['kind'] != 'kbuild': self.log.debug(f"{node['id']} is not a top-level coverage node, skipping") return [] - parsed_coverage_node = self._parse_build_node(origin, build_node) + parsed_node = self._parse_build_node(origin, build_node) + parsed_coverage_node = parsed_node[0] # Add misc fields for coverage results child_nodes = self._api.node.find({'parent': node['id'], 'kind': 'test'}) @@ -588,7 +589,7 @@ def _parse_coverage_node(self, origin, node): self.log.warning(f"No measurement in node '{child['name']}' ({child['id']})") continue field_name = self._replace_restricted_chars(child['name'], r'^[a-zA-Z0-9_]*$') - parsed_coverage_node['misc'][field] = child['data']['misc']['measurement'] + parsed_coverage_node['misc'][field_name] = child['data']['misc']['measurement'] # Add HTML coverage report to the build node's artifacts artifacts = node.get('artifacts') From d8981a2128374c3a1922520d231f75ac18b58803 Mon Sep 17 00:00:00 2001 From: Jeny Sadadia Date: Sat, 23 Aug 2025 14:42:50 +0530 Subject: [PATCH 3/3] Debug logs Signed-off-by: Jeny Sadadia --- src/send_kcidb.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/send_kcidb.py b/src/send_kcidb.py index 594a8c6b9..17aacf93f 100755 --- a/src/send_kcidb.py +++ b/src/send_kcidb.py @@ -567,6 +567,7 @@ def _parse_test_node(self, origin, test_node): return parsed_test_node, dummy_build def _parse_coverage_node(self, origin, node): + self.log.debug(f"Parsing coverage node: {node['id']}") # Coverage report nodes are not sent to the DB themselves, but rather # parsed to submit an updated build node including the functions/lines # coverage percentages as misc fields. @@ -597,6 +598,7 @@ def _parse_coverage_node(self, origin, node): parsed_coverage_node['misc']['coverage_report_url'] = artifacts.get('coverage_report') parsed_coverage_node['misc']['coverage_log_url'] = artifacts.get('log') + self.log.debug(f"Parsed coverage node: {parsed_coverage_node}") return [parsed_coverage_node] def _get_test_data(self, node, origin,