mirror of
https://github.com/torvalds/linux.git
synced 2026-04-22 16:53:59 -04:00
The current logic was storing symbols source code on a list, not linked to the actual KdocItem. While this works fine when kernel-doc markups are OK, on places where there is a "/**" without a valid kernel-doc markup, it ends that the 1:1 match between source code and KdocItem doesn't happen, causing problems to generate the YAML output. Fix it by storing the source code directly into the KdocItem structure. This shouldn't affect performance or memory footprint, except when --yaml option is used. While here, add a __repr__() function for KdocItem, as it helps debugging it. Signed-off-by: Mauro Carvalho Chehab <mchehab+huawei@kernel.org> Signed-off-by: Jonathan Corbet <corbet@lwn.net> Message-ID: <77902dafabb5c3250486aa2dc1568d5fafa95c5b.1774256269.git.mchehab+huawei@kernel.org>
168 lines
4.9 KiB
Python
168 lines
4.9 KiB
Python
#!/usr/bin/env python3
|
||
# SPDX-License-Identifier: GPL-2.0
|
||
# Copyright(c) 2026: Mauro Carvalho Chehab <mchehab@kernel.org>.
|
||
|
||
import os
|
||
|
||
from kdoc.kdoc_output import ManFormat, RestFormat
|
||
|
||
|
||
class KDocTestFile():
|
||
"""
|
||
Handles the logic needed to store kernel‑doc output inside a YAML file.
|
||
Useful for unit tests and regression tests.
|
||
"""
|
||
|
||
def __init__(self, config, yaml_file, yaml_content):
|
||
#
|
||
# Bail out early if yaml is not available
|
||
#
|
||
try:
|
||
import yaml
|
||
except ImportError:
|
||
sys.exit("Warning: yaml package not available. Aborting it.")
|
||
|
||
self.config = config
|
||
self.test_file = os.path.expanduser(yaml_file)
|
||
self.yaml_content = yaml_content
|
||
|
||
self.tests = []
|
||
|
||
out_dir = os.path.dirname(self.test_file)
|
||
if out_dir and not os.path.isdir(out_dir):
|
||
sys.exit(f"Directory {out_dir} doesn't exist.")
|
||
|
||
self.out_style = []
|
||
|
||
if "man" in self.yaml_content:
|
||
out_style = ManFormat()
|
||
out_style.set_config(self.config)
|
||
|
||
self.out_style.append(out_style)
|
||
|
||
if "rst" in self.yaml_content:
|
||
out_style = RestFormat()
|
||
out_style.set_config(self.config)
|
||
|
||
self.out_style.append(out_style)
|
||
|
||
def set_filter(self, export, internal, symbol, nosymbol,
|
||
function_table, enable_lineno, no_doc_sections):
|
||
"""
|
||
Set filters at the output classes.
|
||
"""
|
||
for out_style in self.out_style:
|
||
out_style.set_filter(export, internal, symbol,
|
||
nosymbol, function_table,
|
||
enable_lineno, no_doc_sections)
|
||
|
||
@staticmethod
|
||
def get_kdoc_item(arg, start_line=1):
|
||
|
||
d = vars(arg)
|
||
|
||
declaration_start_line = d.get("declaration_start_line")
|
||
if not declaration_start_line:
|
||
return d
|
||
|
||
d["declaration_start_line"] = start_line
|
||
|
||
parameterdesc_start_lines = d.get("parameterdesc_start_lines")
|
||
if parameterdesc_start_lines:
|
||
for key in parameterdesc_start_lines:
|
||
ln = parameterdesc_start_lines[key]
|
||
ln += start_line - declaration_start_line
|
||
|
||
parameterdesc_start_lines[key] = ln
|
||
|
||
sections_start_lines = d.get("sections_start_lines")
|
||
if sections_start_lines:
|
||
for key in sections_start_lines:
|
||
ln = sections_start_lines[key]
|
||
ln += start_line - declaration_start_line
|
||
|
||
sections_start_lines[key] = ln
|
||
|
||
return d
|
||
|
||
def output_symbols(self, fname, symbols):
|
||
"""
|
||
Store source, symbols and output strings at self.tests.
|
||
"""
|
||
|
||
#
|
||
# KdocItem needs to be converted into dicts
|
||
#
|
||
kdoc_item = []
|
||
expected = []
|
||
|
||
#
|
||
# Source code didn't produce any symbol
|
||
#
|
||
if not symbols:
|
||
return
|
||
|
||
base_name = "test_" + fname.replace(".", "_").replace("/", "_")
|
||
expected_dict = {}
|
||
start_line=1
|
||
|
||
for i in range(0, len(symbols)):
|
||
arg = symbols[i]
|
||
|
||
source = arg.get("source", "")
|
||
|
||
if arg and "KdocItem" in self.yaml_content:
|
||
msg = self.get_kdoc_item(arg)
|
||
|
||
other_stuff = msg.get("other_stuff", {})
|
||
if "source" in other_stuff:
|
||
del other_stuff["source"]
|
||
|
||
expected_dict["kdoc_item"] = msg
|
||
|
||
for out_style in self.out_style:
|
||
if isinstance(out_style, ManFormat):
|
||
key = "man"
|
||
else:
|
||
key = "rst"
|
||
|
||
expected_dict[key]= out_style.output_symbols(fname, [arg]).strip()
|
||
|
||
name = f"{base_name}_{i:03d}"
|
||
|
||
test = {
|
||
"name": name,
|
||
"description": f"{fname} line {arg.declaration_start_line}",
|
||
"fname": fname,
|
||
"source": source,
|
||
"expected": [expected_dict]
|
||
}
|
||
|
||
self.tests.append(test)
|
||
|
||
expected_dict = {}
|
||
|
||
def write(self):
|
||
"""
|
||
Output the content of self.tests to self.test_file.
|
||
"""
|
||
import yaml
|
||
|
||
# Helper function to better handle multilines
|
||
def str_presenter(dumper, data):
|
||
if "\n" in data:
|
||
return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|")
|
||
|
||
return dumper.represent_scalar("tag:yaml.org,2002:str", data)
|
||
|
||
# Register the representer
|
||
yaml.add_representer(str, str_presenter)
|
||
|
||
data = {"tests": self.tests}
|
||
|
||
with open(self.test_file, "w", encoding="utf-8") as fp:
|
||
yaml.dump(data, fp,
|
||
sort_keys=False, width=120, indent=2,
|
||
default_flow_style=False, allow_unicode=True,
|
||
explicit_start=False, explicit_end=False)
|