xref: /linux/tools/lib/python/kdoc/kdoc_yaml_file.py (revision 6e0d7b63676b85490bbaf01c9a8ebcd692bed981)
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