1b37b3cbbSMauro Carvalho Chehab#!/usr/bin/env python3 2b37b3cbbSMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0 3b37b3cbbSMauro Carvalho Chehab# Copyright(c) 2026: Mauro Carvalho Chehab <mchehab@kernel.org>. 4b37b3cbbSMauro Carvalho Chehab 5b37b3cbbSMauro Carvalho Chehabimport os 6b37b3cbbSMauro Carvalho Chehab 7b37b3cbbSMauro Carvalho Chehabfrom kdoc.kdoc_output import ManFormat, RestFormat 8b37b3cbbSMauro Carvalho Chehab 9b37b3cbbSMauro Carvalho Chehab 10b37b3cbbSMauro Carvalho Chehabclass KDocTestFile(): 11b37b3cbbSMauro Carvalho Chehab """ 12b37b3cbbSMauro Carvalho Chehab Handles the logic needed to store kernel‑doc output inside a YAML file. 13b37b3cbbSMauro Carvalho Chehab Useful for unit tests and regression tests. 14b37b3cbbSMauro Carvalho Chehab """ 15b37b3cbbSMauro Carvalho Chehab 16b37b3cbbSMauro Carvalho Chehab def __init__(self, config, yaml_file, yaml_content): 17b37b3cbbSMauro Carvalho Chehab # 18b37b3cbbSMauro Carvalho Chehab # Bail out early if yaml is not available 19b37b3cbbSMauro Carvalho Chehab # 20b37b3cbbSMauro Carvalho Chehab try: 21b37b3cbbSMauro Carvalho Chehab import yaml 22b37b3cbbSMauro Carvalho Chehab except ImportError: 23b37b3cbbSMauro Carvalho Chehab sys.exit("Warning: yaml package not available. Aborting it.") 24b37b3cbbSMauro Carvalho Chehab 25b37b3cbbSMauro Carvalho Chehab self.config = config 26b37b3cbbSMauro Carvalho Chehab self.test_file = os.path.expanduser(yaml_file) 27b37b3cbbSMauro Carvalho Chehab self.yaml_content = yaml_content 28*e786fab2SMauro Carvalho Chehab self.test_names = set() 29b37b3cbbSMauro Carvalho Chehab 30b37b3cbbSMauro Carvalho Chehab self.tests = [] 31b37b3cbbSMauro Carvalho Chehab 32b37b3cbbSMauro Carvalho Chehab out_dir = os.path.dirname(self.test_file) 33b37b3cbbSMauro Carvalho Chehab if out_dir and not os.path.isdir(out_dir): 34b37b3cbbSMauro Carvalho Chehab sys.exit(f"Directory {out_dir} doesn't exist.") 35b37b3cbbSMauro Carvalho Chehab 36b37b3cbbSMauro Carvalho Chehab self.out_style = [] 37b37b3cbbSMauro Carvalho Chehab 38b37b3cbbSMauro Carvalho Chehab if "man" in self.yaml_content: 39b37b3cbbSMauro Carvalho Chehab out_style = ManFormat() 40b37b3cbbSMauro Carvalho Chehab out_style.set_config(self.config) 41b37b3cbbSMauro Carvalho Chehab 42b37b3cbbSMauro Carvalho Chehab self.out_style.append(out_style) 43b37b3cbbSMauro Carvalho Chehab 44b37b3cbbSMauro Carvalho Chehab if "rst" in self.yaml_content: 45b37b3cbbSMauro Carvalho Chehab out_style = RestFormat() 46b37b3cbbSMauro Carvalho Chehab out_style.set_config(self.config) 47b37b3cbbSMauro Carvalho Chehab 48b37b3cbbSMauro Carvalho Chehab self.out_style.append(out_style) 49b37b3cbbSMauro Carvalho Chehab 50b37b3cbbSMauro Carvalho Chehab def set_filter(self, export, internal, symbol, nosymbol, 51b37b3cbbSMauro Carvalho Chehab function_table, enable_lineno, no_doc_sections): 52b37b3cbbSMauro Carvalho Chehab """ 53b37b3cbbSMauro Carvalho Chehab Set filters at the output classes. 54b37b3cbbSMauro Carvalho Chehab """ 55b37b3cbbSMauro Carvalho Chehab for out_style in self.out_style: 56b37b3cbbSMauro Carvalho Chehab out_style.set_filter(export, internal, symbol, 57b37b3cbbSMauro Carvalho Chehab nosymbol, function_table, 58b37b3cbbSMauro Carvalho Chehab enable_lineno, no_doc_sections) 59b37b3cbbSMauro Carvalho Chehab 60b37b3cbbSMauro Carvalho Chehab @staticmethod 61b37b3cbbSMauro Carvalho Chehab def get_kdoc_item(arg, start_line=1): 62b37b3cbbSMauro Carvalho Chehab 63b37b3cbbSMauro Carvalho Chehab d = vars(arg) 64b37b3cbbSMauro Carvalho Chehab 65b37b3cbbSMauro Carvalho Chehab declaration_start_line = d.get("declaration_start_line") 66b37b3cbbSMauro Carvalho Chehab if not declaration_start_line: 67b37b3cbbSMauro Carvalho Chehab return d 68b37b3cbbSMauro Carvalho Chehab 69b37b3cbbSMauro Carvalho Chehab d["declaration_start_line"] = start_line 70b37b3cbbSMauro Carvalho Chehab 71b37b3cbbSMauro Carvalho Chehab parameterdesc_start_lines = d.get("parameterdesc_start_lines") 72b37b3cbbSMauro Carvalho Chehab if parameterdesc_start_lines: 73b37b3cbbSMauro Carvalho Chehab for key in parameterdesc_start_lines: 74b37b3cbbSMauro Carvalho Chehab ln = parameterdesc_start_lines[key] 75b37b3cbbSMauro Carvalho Chehab ln += start_line - declaration_start_line 76b37b3cbbSMauro Carvalho Chehab 77b37b3cbbSMauro Carvalho Chehab parameterdesc_start_lines[key] = ln 78b37b3cbbSMauro Carvalho Chehab 79b37b3cbbSMauro Carvalho Chehab sections_start_lines = d.get("sections_start_lines") 80b37b3cbbSMauro Carvalho Chehab if sections_start_lines: 81b37b3cbbSMauro Carvalho Chehab for key in sections_start_lines: 82b37b3cbbSMauro Carvalho Chehab ln = sections_start_lines[key] 83b37b3cbbSMauro Carvalho Chehab ln += start_line - declaration_start_line 84b37b3cbbSMauro Carvalho Chehab 85b37b3cbbSMauro Carvalho Chehab sections_start_lines[key] = ln 86b37b3cbbSMauro Carvalho Chehab 87b37b3cbbSMauro Carvalho Chehab return d 88b37b3cbbSMauro Carvalho Chehab 8999ec67a9SMauro Carvalho Chehab def output_symbols(self, fname, symbols): 90b37b3cbbSMauro Carvalho Chehab """ 91b37b3cbbSMauro Carvalho Chehab Store source, symbols and output strings at self.tests. 92b37b3cbbSMauro Carvalho Chehab """ 93b37b3cbbSMauro Carvalho Chehab 94b37b3cbbSMauro Carvalho Chehab # 95b37b3cbbSMauro Carvalho Chehab # KdocItem needs to be converted into dicts 96b37b3cbbSMauro Carvalho Chehab # 97b37b3cbbSMauro Carvalho Chehab kdoc_item = [] 98b37b3cbbSMauro Carvalho Chehab expected = [] 99b37b3cbbSMauro Carvalho Chehab 10099ec67a9SMauro Carvalho Chehab # 10199ec67a9SMauro Carvalho Chehab # Source code didn't produce any symbol 10299ec67a9SMauro Carvalho Chehab # 10399ec67a9SMauro Carvalho Chehab if not symbols: 104b37b3cbbSMauro Carvalho Chehab return 105b37b3cbbSMauro Carvalho Chehab 106b37b3cbbSMauro Carvalho Chehab expected_dict = {} 107b37b3cbbSMauro Carvalho Chehab start_line=1 108b37b3cbbSMauro Carvalho Chehab 109*e786fab2SMauro Carvalho Chehab for arg in symbols: 11099ec67a9SMauro Carvalho Chehab source = arg.get("source", "") 11199ec67a9SMauro Carvalho Chehab 11299ec67a9SMauro Carvalho Chehab if arg and "KdocItem" in self.yaml_content: 113b37b3cbbSMauro Carvalho Chehab msg = self.get_kdoc_item(arg) 114b37b3cbbSMauro Carvalho Chehab 11599ec67a9SMauro Carvalho Chehab other_stuff = msg.get("other_stuff", {}) 11699ec67a9SMauro Carvalho Chehab if "source" in other_stuff: 11799ec67a9SMauro Carvalho Chehab del other_stuff["source"] 11899ec67a9SMauro Carvalho Chehab 119b37b3cbbSMauro Carvalho Chehab expected_dict["kdoc_item"] = msg 120b37b3cbbSMauro Carvalho Chehab 121*e786fab2SMauro Carvalho Chehab base_name = arg.name 122*e786fab2SMauro Carvalho Chehab if not base_name: 123*e786fab2SMauro Carvalho Chehab base_name = fname 124*e786fab2SMauro Carvalho Chehab base_name = base_name.lower().replace(".", "_").replace("/", "_") 125*e786fab2SMauro Carvalho Chehab 126*e786fab2SMauro Carvalho Chehab 127*e786fab2SMauro Carvalho Chehab # Don't add duplicated names 128*e786fab2SMauro Carvalho Chehab i = 0 129*e786fab2SMauro Carvalho Chehab name = base_name 130*e786fab2SMauro Carvalho Chehab while name in self.test_names: 131*e786fab2SMauro Carvalho Chehab i += 1 132*e786fab2SMauro Carvalho Chehab name = f"{base_name}_{i:03d}" 133*e786fab2SMauro Carvalho Chehab 134*e786fab2SMauro Carvalho Chehab self.test_names.add(name) 135*e786fab2SMauro Carvalho Chehab 136b37b3cbbSMauro Carvalho Chehab for out_style in self.out_style: 137b37b3cbbSMauro Carvalho Chehab if isinstance(out_style, ManFormat): 138b37b3cbbSMauro Carvalho Chehab key = "man" 139b37b3cbbSMauro Carvalho Chehab else: 140b37b3cbbSMauro Carvalho Chehab key = "rst" 141b37b3cbbSMauro Carvalho Chehab 1426e0d7b63SMauro Carvalho Chehab expected_dict[key]= out_style.output_symbols(fname, [arg]).strip() 143b37b3cbbSMauro Carvalho Chehab 144b37b3cbbSMauro Carvalho Chehab test = { 145b37b3cbbSMauro Carvalho Chehab "name": name, 14699ec67a9SMauro Carvalho Chehab "description": f"{fname} line {arg.declaration_start_line}", 147b37b3cbbSMauro Carvalho Chehab "fname": fname, 14899ec67a9SMauro Carvalho Chehab "source": source, 149b37b3cbbSMauro Carvalho Chehab "expected": [expected_dict] 150b37b3cbbSMauro Carvalho Chehab } 151b37b3cbbSMauro Carvalho Chehab 152b37b3cbbSMauro Carvalho Chehab self.tests.append(test) 153b37b3cbbSMauro Carvalho Chehab 154b37b3cbbSMauro Carvalho Chehab expected_dict = {} 155b37b3cbbSMauro Carvalho Chehab 156b37b3cbbSMauro Carvalho Chehab def write(self): 157b37b3cbbSMauro Carvalho Chehab """ 158b37b3cbbSMauro Carvalho Chehab Output the content of self.tests to self.test_file. 159b37b3cbbSMauro Carvalho Chehab """ 160b37b3cbbSMauro Carvalho Chehab import yaml 161b37b3cbbSMauro Carvalho Chehab 1626e0d7b63SMauro Carvalho Chehab # Helper function to better handle multilines 1636e0d7b63SMauro Carvalho Chehab def str_presenter(dumper, data): 1646e0d7b63SMauro Carvalho Chehab if "\n" in data: 1656e0d7b63SMauro Carvalho Chehab return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|") 1666e0d7b63SMauro Carvalho Chehab 1676e0d7b63SMauro Carvalho Chehab return dumper.represent_scalar("tag:yaml.org,2002:str", data) 1686e0d7b63SMauro Carvalho Chehab 1696e0d7b63SMauro Carvalho Chehab # Register the representer 1706e0d7b63SMauro Carvalho Chehab yaml.add_representer(str, str_presenter) 1716e0d7b63SMauro Carvalho Chehab 172b37b3cbbSMauro Carvalho Chehab data = {"tests": self.tests} 173b37b3cbbSMauro Carvalho Chehab 174b37b3cbbSMauro Carvalho Chehab with open(self.test_file, "w", encoding="utf-8") as fp: 1756e0d7b63SMauro Carvalho Chehab yaml.dump(data, fp, 1766e0d7b63SMauro Carvalho Chehab sort_keys=False, width=120, indent=2, 1776e0d7b63SMauro Carvalho Chehab default_flow_style=False, allow_unicode=True, 1786e0d7b63SMauro Carvalho Chehab explicit_start=False, explicit_end=False) 179