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 28b37b3cbbSMauro Carvalho Chehab 29b37b3cbbSMauro Carvalho Chehab self.tests = [] 30b37b3cbbSMauro Carvalho Chehab 31b37b3cbbSMauro Carvalho Chehab out_dir = os.path.dirname(self.test_file) 32b37b3cbbSMauro Carvalho Chehab if out_dir and not os.path.isdir(out_dir): 33b37b3cbbSMauro Carvalho Chehab sys.exit(f"Directory {out_dir} doesn't exist.") 34b37b3cbbSMauro Carvalho Chehab 35b37b3cbbSMauro Carvalho Chehab self.out_style = [] 36b37b3cbbSMauro Carvalho Chehab 37b37b3cbbSMauro Carvalho Chehab if "man" in self.yaml_content: 38b37b3cbbSMauro Carvalho Chehab out_style = ManFormat() 39b37b3cbbSMauro Carvalho Chehab out_style.set_config(self.config) 40b37b3cbbSMauro Carvalho Chehab 41b37b3cbbSMauro Carvalho Chehab self.out_style.append(out_style) 42b37b3cbbSMauro Carvalho Chehab 43b37b3cbbSMauro Carvalho Chehab if "rst" in self.yaml_content: 44b37b3cbbSMauro Carvalho Chehab out_style = RestFormat() 45b37b3cbbSMauro Carvalho Chehab out_style.set_config(self.config) 46b37b3cbbSMauro Carvalho Chehab 47b37b3cbbSMauro Carvalho Chehab self.out_style.append(out_style) 48b37b3cbbSMauro Carvalho Chehab 49b37b3cbbSMauro Carvalho Chehab def set_filter(self, export, internal, symbol, nosymbol, 50b37b3cbbSMauro Carvalho Chehab function_table, enable_lineno, no_doc_sections): 51b37b3cbbSMauro Carvalho Chehab """ 52b37b3cbbSMauro Carvalho Chehab Set filters at the output classes. 53b37b3cbbSMauro Carvalho Chehab """ 54b37b3cbbSMauro Carvalho Chehab for out_style in self.out_style: 55b37b3cbbSMauro Carvalho Chehab out_style.set_filter(export, internal, symbol, 56b37b3cbbSMauro Carvalho Chehab nosymbol, function_table, 57b37b3cbbSMauro Carvalho Chehab enable_lineno, no_doc_sections) 58b37b3cbbSMauro Carvalho Chehab 59b37b3cbbSMauro Carvalho Chehab @staticmethod 60b37b3cbbSMauro Carvalho Chehab def get_kdoc_item(arg, start_line=1): 61b37b3cbbSMauro Carvalho Chehab 62b37b3cbbSMauro Carvalho Chehab d = vars(arg) 63b37b3cbbSMauro Carvalho Chehab 64b37b3cbbSMauro Carvalho Chehab declaration_start_line = d.get("declaration_start_line") 65b37b3cbbSMauro Carvalho Chehab if not declaration_start_line: 66b37b3cbbSMauro Carvalho Chehab return d 67b37b3cbbSMauro Carvalho Chehab 68b37b3cbbSMauro Carvalho Chehab d["declaration_start_line"] = start_line 69b37b3cbbSMauro Carvalho Chehab 70b37b3cbbSMauro Carvalho Chehab parameterdesc_start_lines = d.get("parameterdesc_start_lines") 71b37b3cbbSMauro Carvalho Chehab if parameterdesc_start_lines: 72b37b3cbbSMauro Carvalho Chehab for key in parameterdesc_start_lines: 73b37b3cbbSMauro Carvalho Chehab ln = parameterdesc_start_lines[key] 74b37b3cbbSMauro Carvalho Chehab ln += start_line - declaration_start_line 75b37b3cbbSMauro Carvalho Chehab 76b37b3cbbSMauro Carvalho Chehab parameterdesc_start_lines[key] = ln 77b37b3cbbSMauro Carvalho Chehab 78b37b3cbbSMauro Carvalho Chehab sections_start_lines = d.get("sections_start_lines") 79b37b3cbbSMauro Carvalho Chehab if sections_start_lines: 80b37b3cbbSMauro Carvalho Chehab for key in sections_start_lines: 81b37b3cbbSMauro Carvalho Chehab ln = sections_start_lines[key] 82b37b3cbbSMauro Carvalho Chehab ln += start_line - declaration_start_line 83b37b3cbbSMauro Carvalho Chehab 84b37b3cbbSMauro Carvalho Chehab sections_start_lines[key] = ln 85b37b3cbbSMauro Carvalho Chehab 86b37b3cbbSMauro Carvalho Chehab return d 87b37b3cbbSMauro Carvalho Chehab 88b37b3cbbSMauro Carvalho Chehab def output_symbols(self, fname, symbols, source): 89b37b3cbbSMauro Carvalho Chehab """ 90b37b3cbbSMauro Carvalho Chehab Store source, symbols and output strings at self.tests. 91b37b3cbbSMauro Carvalho Chehab """ 92b37b3cbbSMauro Carvalho Chehab 93b37b3cbbSMauro Carvalho Chehab # 94b37b3cbbSMauro Carvalho Chehab # KdocItem needs to be converted into dicts 95b37b3cbbSMauro Carvalho Chehab # 96b37b3cbbSMauro Carvalho Chehab kdoc_item = [] 97b37b3cbbSMauro Carvalho Chehab expected = [] 98b37b3cbbSMauro Carvalho Chehab 99b37b3cbbSMauro Carvalho Chehab if not symbols and not source: 100b37b3cbbSMauro Carvalho Chehab return 101b37b3cbbSMauro Carvalho Chehab 102b37b3cbbSMauro Carvalho Chehab if not source or len(symbols) != len(source): 103b37b3cbbSMauro Carvalho Chehab print(f"Warning: lengths are different. Ignoring {fname}") 104b37b3cbbSMauro Carvalho Chehab 105b37b3cbbSMauro Carvalho Chehab # Folding without line numbers is too hard. 106b37b3cbbSMauro Carvalho Chehab # The right thing to do here to proceed would be to delete 107b37b3cbbSMauro Carvalho Chehab # not-handled source blocks, as len(source) should be bigger 108b37b3cbbSMauro Carvalho Chehab # than len(symbols) 109b37b3cbbSMauro Carvalho Chehab return 110b37b3cbbSMauro Carvalho Chehab 111b37b3cbbSMauro Carvalho Chehab base_name = "test_" + fname.replace(".", "_").replace("/", "_") 112b37b3cbbSMauro Carvalho Chehab expected_dict = {} 113b37b3cbbSMauro Carvalho Chehab start_line=1 114b37b3cbbSMauro Carvalho Chehab 115b37b3cbbSMauro Carvalho Chehab for i in range(0, len(symbols)): 116b37b3cbbSMauro Carvalho Chehab arg = symbols[i] 117b37b3cbbSMauro Carvalho Chehab 118b37b3cbbSMauro Carvalho Chehab if "KdocItem" in self.yaml_content: 119b37b3cbbSMauro Carvalho Chehab msg = self.get_kdoc_item(arg) 120b37b3cbbSMauro Carvalho Chehab 121b37b3cbbSMauro Carvalho Chehab expected_dict["kdoc_item"] = msg 122b37b3cbbSMauro Carvalho Chehab 123b37b3cbbSMauro Carvalho Chehab for out_style in self.out_style: 124b37b3cbbSMauro Carvalho Chehab if isinstance(out_style, ManFormat): 125b37b3cbbSMauro Carvalho Chehab key = "man" 126b37b3cbbSMauro Carvalho Chehab else: 127b37b3cbbSMauro Carvalho Chehab key = "rst" 128b37b3cbbSMauro Carvalho Chehab 129*6e0d7b63SMauro Carvalho Chehab expected_dict[key]= out_style.output_symbols(fname, [arg]).strip() 130b37b3cbbSMauro Carvalho Chehab 131b37b3cbbSMauro Carvalho Chehab name = f"{base_name}_{i:03d}" 132b37b3cbbSMauro Carvalho Chehab 133b37b3cbbSMauro Carvalho Chehab test = { 134b37b3cbbSMauro Carvalho Chehab "name": name, 135b37b3cbbSMauro Carvalho Chehab "description": f"{fname} line {source[i]["ln"]}", 136b37b3cbbSMauro Carvalho Chehab "fname": fname, 137b37b3cbbSMauro Carvalho Chehab "source": source[i]["data"], 138b37b3cbbSMauro Carvalho Chehab "expected": [expected_dict] 139b37b3cbbSMauro Carvalho Chehab } 140b37b3cbbSMauro Carvalho Chehab 141b37b3cbbSMauro Carvalho Chehab self.tests.append(test) 142b37b3cbbSMauro Carvalho Chehab 143b37b3cbbSMauro Carvalho Chehab expected_dict = {} 144b37b3cbbSMauro Carvalho Chehab 145b37b3cbbSMauro Carvalho Chehab def write(self): 146b37b3cbbSMauro Carvalho Chehab """ 147b37b3cbbSMauro Carvalho Chehab Output the content of self.tests to self.test_file. 148b37b3cbbSMauro Carvalho Chehab """ 149b37b3cbbSMauro Carvalho Chehab import yaml 150b37b3cbbSMauro Carvalho Chehab 151*6e0d7b63SMauro Carvalho Chehab # Helper function to better handle multilines 152*6e0d7b63SMauro Carvalho Chehab def str_presenter(dumper, data): 153*6e0d7b63SMauro Carvalho Chehab if "\n" in data: 154*6e0d7b63SMauro Carvalho Chehab return dumper.represent_scalar("tag:yaml.org,2002:str", data, style="|") 155*6e0d7b63SMauro Carvalho Chehab 156*6e0d7b63SMauro Carvalho Chehab return dumper.represent_scalar("tag:yaml.org,2002:str", data) 157*6e0d7b63SMauro Carvalho Chehab 158*6e0d7b63SMauro Carvalho Chehab # Register the representer 159*6e0d7b63SMauro Carvalho Chehab yaml.add_representer(str, str_presenter) 160*6e0d7b63SMauro Carvalho Chehab 161b37b3cbbSMauro Carvalho Chehab data = {"tests": self.tests} 162b37b3cbbSMauro Carvalho Chehab 163b37b3cbbSMauro Carvalho Chehab with open(self.test_file, "w", encoding="utf-8") as fp: 164*6e0d7b63SMauro Carvalho Chehab yaml.dump(data, fp, 165*6e0d7b63SMauro Carvalho Chehab sort_keys=False, width=120, indent=2, 166*6e0d7b63SMauro Carvalho Chehab default_flow_style=False, allow_unicode=True, 167*6e0d7b63SMauro Carvalho Chehab explicit_start=False, explicit_end=False) 168