xref: /linux/tools/unittests/test_kdoc_parser.py (revision eea0d807dbd42b411ae0df21c6ca8d3560cb9fbf)
1a5dea56eSMauro Carvalho Chehab#!/usr/bin/env python3
2a5dea56eSMauro Carvalho Chehab# SPDX-License-Identifier: GPL-2.0
3a5dea56eSMauro Carvalho Chehab# Copyright(c) 2026: Mauro Carvalho Chehab <mchehab@kernel.org>.
4a5dea56eSMauro Carvalho Chehab#
5a5dea56eSMauro Carvalho Chehab# pylint: disable=C0200,C0413,W0102,R0914
6a5dea56eSMauro Carvalho Chehab
7a5dea56eSMauro Carvalho Chehab"""
8a5dea56eSMauro Carvalho ChehabUnit tests for kernel-doc parser.
9a5dea56eSMauro Carvalho Chehab"""
10a5dea56eSMauro Carvalho Chehab
11*eea0d807SMauro Carvalho Chehabimport logging
12a5dea56eSMauro Carvalho Chehabimport os
13a5dea56eSMauro Carvalho Chehabimport re
14*eea0d807SMauro Carvalho Chehabimport shlex
15a5dea56eSMauro Carvalho Chehabimport sys
16*eea0d807SMauro Carvalho Chehabimport unittest
17a5dea56eSMauro Carvalho Chehab
18a5dea56eSMauro Carvalho Chehabfrom textwrap import dedent
19a5dea56eSMauro Carvalho Chehabfrom unittest.mock import patch, MagicMock, mock_open
20a5dea56eSMauro Carvalho Chehab
21*eea0d807SMauro Carvalho Chehabimport yaml
22*eea0d807SMauro Carvalho Chehab
23a5dea56eSMauro Carvalho ChehabSRC_DIR = os.path.dirname(os.path.realpath(__file__))
24a5dea56eSMauro Carvalho Chehabsys.path.insert(0, os.path.join(SRC_DIR, "../lib/python"))
25a5dea56eSMauro Carvalho Chehab
26*eea0d807SMauro Carvalho Chehabfrom kdoc.kdoc_files import KdocConfig
27a5dea56eSMauro Carvalho Chehabfrom kdoc.kdoc_item import KdocItem
28*eea0d807SMauro Carvalho Chehabfrom kdoc.kdoc_parser import KernelDoc
29*eea0d807SMauro Carvalho Chehabfrom kdoc.kdoc_output import RestFormat, ManFormat
30*eea0d807SMauro Carvalho Chehab
31a5dea56eSMauro Carvalho Chehabfrom kdoc.xforms_lists import CTransforms
32*eea0d807SMauro Carvalho Chehab
33a5dea56eSMauro Carvalho Chehabfrom unittest_helper import run_unittest
34a5dea56eSMauro Carvalho Chehab
35*eea0d807SMauro Carvalho Chehab
36*eea0d807SMauro Carvalho Chehab#
37*eea0d807SMauro Carvalho Chehab# Test file
38*eea0d807SMauro Carvalho Chehab#
39*eea0d807SMauro Carvalho ChehabTEST_FILE = os.path.join(SRC_DIR, "kdoc-test.yaml")
40*eea0d807SMauro Carvalho Chehab
41*eea0d807SMauro Carvalho Chehab#
42*eea0d807SMauro Carvalho Chehab# Ancillary logic to clean whitespaces
43*eea0d807SMauro Carvalho Chehab#
44a5dea56eSMauro Carvalho Chehab#: Regex to help cleaning whitespaces
45*eea0d807SMauro Carvalho ChehabRE_WHITESPC = re.compile(r"[ \t]++")
46*eea0d807SMauro Carvalho ChehabRE_BEGINSPC = re.compile(r"^\s+", re.MULTILINE)
47*eea0d807SMauro Carvalho ChehabRE_ENDSPC = re.compile(r"\s+$", re.MULTILINE)
48a5dea56eSMauro Carvalho Chehab
49a5dea56eSMauro Carvalho Chehabdef clean_whitespc(val, relax_whitespace=False):
50a5dea56eSMauro Carvalho Chehab    """
51a5dea56eSMauro Carvalho Chehab    Cleanup whitespaces to avoid false positives.
52a5dea56eSMauro Carvalho Chehab
53a5dea56eSMauro Carvalho Chehab    By default, strip only bein/end whitespaces, but, when relax_whitespace
54a5dea56eSMauro Carvalho Chehab    is true, also replace multiple whitespaces in the middle.
55a5dea56eSMauro Carvalho Chehab    """
56a5dea56eSMauro Carvalho Chehab
57a5dea56eSMauro Carvalho Chehab    if isinstance(val, str):
58a5dea56eSMauro Carvalho Chehab        val = val.strip()
59a5dea56eSMauro Carvalho Chehab        if relax_whitespace:
60a5dea56eSMauro Carvalho Chehab            val = RE_WHITESPC.sub(" ", val)
61*eea0d807SMauro Carvalho Chehab            val = RE_BEGINSPC.sub("", val)
62*eea0d807SMauro Carvalho Chehab            val = RE_ENDSPC.sub("", val)
63a5dea56eSMauro Carvalho Chehab    elif isinstance(val, list):
64a5dea56eSMauro Carvalho Chehab        val = [clean_whitespc(item, relax_whitespace) for item in val]
65a5dea56eSMauro Carvalho Chehab    elif isinstance(val, dict):
66a5dea56eSMauro Carvalho Chehab        val = {k: clean_whitespc(v, relax_whitespace) for k, v in val.items()}
67a5dea56eSMauro Carvalho Chehab    return val
68a5dea56eSMauro Carvalho Chehab
69a5dea56eSMauro Carvalho Chehab#
70*eea0d807SMauro Carvalho Chehab# Helper classes to help mocking with logger and config
71a5dea56eSMauro Carvalho Chehab#
72*eea0d807SMauro Carvalho Chehabclass MockLogging(logging.Handler):
73*eea0d807SMauro Carvalho Chehab    """
74*eea0d807SMauro Carvalho Chehab    Simple class to store everything on a list
75*eea0d807SMauro Carvalho Chehab    """
76*eea0d807SMauro Carvalho Chehab
77*eea0d807SMauro Carvalho Chehab    def __init__(self, level=logging.NOTSET):
78*eea0d807SMauro Carvalho Chehab        super().__init__(level)
79*eea0d807SMauro Carvalho Chehab        self.messages = []
80*eea0d807SMauro Carvalho Chehab        self.formatter = logging.Formatter()
81*eea0d807SMauro Carvalho Chehab
82*eea0d807SMauro Carvalho Chehab    def emit(self, record: logging.LogRecord) -> None:
83*eea0d807SMauro Carvalho Chehab        """
84*eea0d807SMauro Carvalho Chehab        Append a formatted record to self.messages.
85*eea0d807SMauro Carvalho Chehab        """
86*eea0d807SMauro Carvalho Chehab        try:
87*eea0d807SMauro Carvalho Chehab            # The `format` method uses the handler's formatter.
88*eea0d807SMauro Carvalho Chehab            message = self.format(record)
89*eea0d807SMauro Carvalho Chehab            self.messages.append(message)
90*eea0d807SMauro Carvalho Chehab        except Exception:
91*eea0d807SMauro Carvalho Chehab            self.handleError(record)
92*eea0d807SMauro Carvalho Chehab
93*eea0d807SMauro Carvalho Chehabclass MockKdocConfig(KdocConfig):
94*eea0d807SMauro Carvalho Chehab    def __init__(self, *args, **kwargs):
95*eea0d807SMauro Carvalho Chehab        super().__init__(*args, **kwargs)
96*eea0d807SMauro Carvalho Chehab
97*eea0d807SMauro Carvalho Chehab        self.log = logging.getLogger(__file__)
98*eea0d807SMauro Carvalho Chehab        self.handler = MockLogging()
99*eea0d807SMauro Carvalho Chehab        self.log.addHandler(self.handler)
100*eea0d807SMauro Carvalho Chehab
101*eea0d807SMauro Carvalho Chehab    def warning(self, msg):
102*eea0d807SMauro Carvalho Chehab        """Ancillary routine to output a warning and increment error count."""
103*eea0d807SMauro Carvalho Chehab
104*eea0d807SMauro Carvalho Chehab        self.log.warning(msg)
105*eea0d807SMauro Carvalho Chehab
106*eea0d807SMauro Carvalho Chehab#
107*eea0d807SMauro Carvalho Chehab# Helper class to generate KdocItem and validate its contents
108*eea0d807SMauro Carvalho Chehab#
109*eea0d807SMauro Carvalho Chehab# TODO: check self.config.handler.messages content
110*eea0d807SMauro Carvalho Chehab#
111*eea0d807SMauro Carvalho Chehabclass GenerateKdocItem(unittest.TestCase):
112a5dea56eSMauro Carvalho Chehab    """
113a5dea56eSMauro Carvalho Chehab    Base class to run KernelDoc parser class
114a5dea56eSMauro Carvalho Chehab    """
115a5dea56eSMauro Carvalho Chehab
116a5dea56eSMauro Carvalho Chehab    DEFAULT = vars(KdocItem("", "", "", 0))
117a5dea56eSMauro Carvalho Chehab
118*eea0d807SMauro Carvalho Chehab    config = MockKdocConfig()
119*eea0d807SMauro Carvalho Chehab    xforms = CTransforms()
120*eea0d807SMauro Carvalho Chehab
121a5dea56eSMauro Carvalho Chehab    def setUp(self):
122a5dea56eSMauro Carvalho Chehab        self.maxDiff = None
123a5dea56eSMauro Carvalho Chehab
124a5dea56eSMauro Carvalho Chehab    def run_test(self, source, __expected_list, exports={}, fname="test.c",
125a5dea56eSMauro Carvalho Chehab                 relax_whitespace=False):
126a5dea56eSMauro Carvalho Chehab        """
127a5dea56eSMauro Carvalho Chehab        Stores expected values and patch the test to use source as
128a5dea56eSMauro Carvalho Chehab        a "file" input.
129a5dea56eSMauro Carvalho Chehab        """
130a5dea56eSMauro Carvalho Chehab        debug_level = int(os.getenv("VERBOSE", "0"))
131a5dea56eSMauro Carvalho Chehab        source = dedent(source)
132a5dea56eSMauro Carvalho Chehab
133a5dea56eSMauro Carvalho Chehab        # Ensure that default values will be there
134a5dea56eSMauro Carvalho Chehab        expected_list = []
135a5dea56eSMauro Carvalho Chehab        for e in __expected_list:
136*eea0d807SMauro Carvalho Chehab            if not isinstance(e, dict):
137*eea0d807SMauro Carvalho Chehab                e = vars(e)
138*eea0d807SMauro Carvalho Chehab
139a5dea56eSMauro Carvalho Chehab            new_e = self.DEFAULT.copy()
140a5dea56eSMauro Carvalho Chehab            new_e["fname"] = fname
141a5dea56eSMauro Carvalho Chehab            for key, value in e.items():
142a5dea56eSMauro Carvalho Chehab                new_e[key] = value
143a5dea56eSMauro Carvalho Chehab
144a5dea56eSMauro Carvalho Chehab            expected_list.append(new_e)
145a5dea56eSMauro Carvalho Chehab
146a5dea56eSMauro Carvalho Chehab        patcher = patch('builtins.open',
147a5dea56eSMauro Carvalho Chehab                        new_callable=mock_open, read_data=source)
148a5dea56eSMauro Carvalho Chehab
149a5dea56eSMauro Carvalho Chehab        kernel_doc = KernelDoc(self.config, fname, self.xforms)
150a5dea56eSMauro Carvalho Chehab
151a5dea56eSMauro Carvalho Chehab        with patcher:
152a5dea56eSMauro Carvalho Chehab            export_table, entries = kernel_doc.parse_kdoc()
153a5dea56eSMauro Carvalho Chehab
154a5dea56eSMauro Carvalho Chehab            self.assertEqual(export_table, exports)
155a5dea56eSMauro Carvalho Chehab            self.assertEqual(len(entries), len(expected_list))
156a5dea56eSMauro Carvalho Chehab
157a5dea56eSMauro Carvalho Chehab            for i in range(0, len(entries)):
158a5dea56eSMauro Carvalho Chehab
159a5dea56eSMauro Carvalho Chehab                entry = entries[i]
160a5dea56eSMauro Carvalho Chehab                expected = expected_list[i]
161a5dea56eSMauro Carvalho Chehab                self.assertNotEqual(expected, None)
162a5dea56eSMauro Carvalho Chehab                self.assertNotEqual(expected, {})
163a5dea56eSMauro Carvalho Chehab                self.assertIsInstance(entry, KdocItem)
164a5dea56eSMauro Carvalho Chehab
165a5dea56eSMauro Carvalho Chehab                d = vars(entry)
166a5dea56eSMauro Carvalho Chehab                for key, value in expected.items():
167a5dea56eSMauro Carvalho Chehab                    result = clean_whitespc(d[key], relax_whitespace)
168a5dea56eSMauro Carvalho Chehab                    value = clean_whitespc(value, relax_whitespace)
169a5dea56eSMauro Carvalho Chehab
170a5dea56eSMauro Carvalho Chehab                    if debug_level > 1:
171a5dea56eSMauro Carvalho Chehab                        sys.stderr.write(f"{key}: assert('{result}' == '{value}')\n")
172a5dea56eSMauro Carvalho Chehab
173a5dea56eSMauro Carvalho Chehab                    self.assertEqual(result, value, msg=f"at {key}")
174a5dea56eSMauro Carvalho Chehab
175*eea0d807SMauro Carvalho Chehab#
176*eea0d807SMauro Carvalho Chehab# Ancillary function that replicates kdoc_files way to generate output
177*eea0d807SMauro Carvalho Chehab#
178*eea0d807SMauro Carvalho Chehabdef cleanup_timestamp(text):
179*eea0d807SMauro Carvalho Chehab    lines = text.split("\n")
180*eea0d807SMauro Carvalho Chehab
181*eea0d807SMauro Carvalho Chehab    for i, line in enumerate(lines):
182*eea0d807SMauro Carvalho Chehab        if not line.startswith('.TH'):
183*eea0d807SMauro Carvalho Chehab            continue
184*eea0d807SMauro Carvalho Chehab
185*eea0d807SMauro Carvalho Chehab        parts = shlex.split(line)
186*eea0d807SMauro Carvalho Chehab        if len(parts) > 3:
187*eea0d807SMauro Carvalho Chehab            parts[3] = ""
188*eea0d807SMauro Carvalho Chehab
189*eea0d807SMauro Carvalho Chehab        lines[i] = " ".join(parts)
190*eea0d807SMauro Carvalho Chehab
191*eea0d807SMauro Carvalho Chehab
192*eea0d807SMauro Carvalho Chehab    return "\n".join(lines)
193*eea0d807SMauro Carvalho Chehab
194*eea0d807SMauro Carvalho Chehabdef gen_output(fname, out_style, symbols, expected,
195*eea0d807SMauro Carvalho Chehab               config=None, relax_whitespace=False):
196*eea0d807SMauro Carvalho Chehab    """
197*eea0d807SMauro Carvalho Chehab    Use the output class to return an output content from KdocItem symbols.
198*eea0d807SMauro Carvalho Chehab    """
199*eea0d807SMauro Carvalho Chehab
200*eea0d807SMauro Carvalho Chehab    if not config:
201*eea0d807SMauro Carvalho Chehab        config = MockKdocConfig()
202*eea0d807SMauro Carvalho Chehab
203*eea0d807SMauro Carvalho Chehab    out_style.set_config(config)
204*eea0d807SMauro Carvalho Chehab
205*eea0d807SMauro Carvalho Chehab    msg = out_style.output_symbols(fname, symbols)
206*eea0d807SMauro Carvalho Chehab
207*eea0d807SMauro Carvalho Chehab    result = clean_whitespc(msg, relax_whitespace)
208*eea0d807SMauro Carvalho Chehab    result = cleanup_timestamp(result)
209*eea0d807SMauro Carvalho Chehab
210*eea0d807SMauro Carvalho Chehab    expected = clean_whitespc(expected, relax_whitespace)
211*eea0d807SMauro Carvalho Chehab    expected = cleanup_timestamp(expected)
212*eea0d807SMauro Carvalho Chehab
213*eea0d807SMauro Carvalho Chehab    return result, expected
214a5dea56eSMauro Carvalho Chehab
215a5dea56eSMauro Carvalho Chehab#
216*eea0d807SMauro Carvalho Chehab# Classes to be used by dynamic test generation from YAML
217a5dea56eSMauro Carvalho Chehab#
218*eea0d807SMauro Carvalho Chehabclass CToKdocItem(GenerateKdocItem):
219*eea0d807SMauro Carvalho Chehab    def setUp(self):
220*eea0d807SMauro Carvalho Chehab        self.maxDiff = None
221*eea0d807SMauro Carvalho Chehab
222*eea0d807SMauro Carvalho Chehab    def run_parser_test(self, source, symbols, exports, fname):
223*eea0d807SMauro Carvalho Chehab        if isinstance(symbols, dict):
224*eea0d807SMauro Carvalho Chehab            symbols = [symbols]
225*eea0d807SMauro Carvalho Chehab
226*eea0d807SMauro Carvalho Chehab        if isinstance(exports, str):
227*eea0d807SMauro Carvalho Chehab            exports=set([exports])
228*eea0d807SMauro Carvalho Chehab        elif isinstance(exports, list):
229*eea0d807SMauro Carvalho Chehab            exports=set(exports)
230*eea0d807SMauro Carvalho Chehab
231*eea0d807SMauro Carvalho Chehab        self.run_test(source, symbols, exports=exports,
232*eea0d807SMauro Carvalho Chehab                      fname=fname, relax_whitespace=True)
233*eea0d807SMauro Carvalho Chehab
234*eea0d807SMauro Carvalho Chehabclass KdocItemToMan(unittest.TestCase):
235*eea0d807SMauro Carvalho Chehab    out_style = ManFormat()
236*eea0d807SMauro Carvalho Chehab
237*eea0d807SMauro Carvalho Chehab    def setUp(self):
238*eea0d807SMauro Carvalho Chehab        self.maxDiff = None
239*eea0d807SMauro Carvalho Chehab
240*eea0d807SMauro Carvalho Chehab    def run_out_test(self, fname, symbols, expected):
241a5dea56eSMauro Carvalho Chehab        """
242*eea0d807SMauro Carvalho Chehab        Generate output using out_style,
243*eea0d807SMauro Carvalho Chehab        """
244*eea0d807SMauro Carvalho Chehab        result, expected = gen_output(fname, self.out_style,
245*eea0d807SMauro Carvalho Chehab                                      symbols, expected)
246*eea0d807SMauro Carvalho Chehab
247*eea0d807SMauro Carvalho Chehab        self.assertEqual(result, expected)
248*eea0d807SMauro Carvalho Chehab
249*eea0d807SMauro Carvalho Chehabclass KdocItemToRest(unittest.TestCase):
250*eea0d807SMauro Carvalho Chehab    out_style = RestFormat()
251*eea0d807SMauro Carvalho Chehab
252*eea0d807SMauro Carvalho Chehab    def setUp(self):
253*eea0d807SMauro Carvalho Chehab        self.maxDiff = None
254*eea0d807SMauro Carvalho Chehab
255*eea0d807SMauro Carvalho Chehab    def run_out_test(self, fname, symbols, expected):
256*eea0d807SMauro Carvalho Chehab        """
257*eea0d807SMauro Carvalho Chehab        Generate output using out_style,
258*eea0d807SMauro Carvalho Chehab        """
259*eea0d807SMauro Carvalho Chehab        result, expected = gen_output(fname, self.out_style, symbols,
260*eea0d807SMauro Carvalho Chehab                                      expected, relax_whitespace=True)
261*eea0d807SMauro Carvalho Chehab
262*eea0d807SMauro Carvalho Chehab        self.assertEqual(result, expected)
263*eea0d807SMauro Carvalho Chehab
264*eea0d807SMauro Carvalho Chehab
265*eea0d807SMauro Carvalho Chehabclass CToMan(unittest.TestCase):
266*eea0d807SMauro Carvalho Chehab    out_style = ManFormat()
267*eea0d807SMauro Carvalho Chehab    config = MockKdocConfig()
268*eea0d807SMauro Carvalho Chehab    xforms = CTransforms()
269*eea0d807SMauro Carvalho Chehab
270*eea0d807SMauro Carvalho Chehab    def setUp(self):
271*eea0d807SMauro Carvalho Chehab        self.maxDiff = None
272*eea0d807SMauro Carvalho Chehab
273*eea0d807SMauro Carvalho Chehab    def run_out_test(self, fname, source, expected):
274*eea0d807SMauro Carvalho Chehab        """
275*eea0d807SMauro Carvalho Chehab        Generate output using out_style,
276*eea0d807SMauro Carvalho Chehab        """
277*eea0d807SMauro Carvalho Chehab        patcher = patch('builtins.open',
278*eea0d807SMauro Carvalho Chehab                        new_callable=mock_open, read_data=source)
279*eea0d807SMauro Carvalho Chehab
280*eea0d807SMauro Carvalho Chehab        kernel_doc = KernelDoc(self.config, fname, self.xforms)
281*eea0d807SMauro Carvalho Chehab
282*eea0d807SMauro Carvalho Chehab        with patcher:
283*eea0d807SMauro Carvalho Chehab            export_table, entries = kernel_doc.parse_kdoc()
284*eea0d807SMauro Carvalho Chehab
285*eea0d807SMauro Carvalho Chehab        result, expected = gen_output(fname, self.out_style,
286*eea0d807SMauro Carvalho Chehab                                      entries, expected, config=self.config)
287*eea0d807SMauro Carvalho Chehab
288*eea0d807SMauro Carvalho Chehab        self.assertEqual(result, expected)
289*eea0d807SMauro Carvalho Chehab
290*eea0d807SMauro Carvalho Chehab
291*eea0d807SMauro Carvalho Chehabclass CToRest(unittest.TestCase):
292*eea0d807SMauro Carvalho Chehab    out_style = RestFormat()
293*eea0d807SMauro Carvalho Chehab    config = MockKdocConfig()
294*eea0d807SMauro Carvalho Chehab    xforms = CTransforms()
295*eea0d807SMauro Carvalho Chehab
296*eea0d807SMauro Carvalho Chehab    def setUp(self):
297*eea0d807SMauro Carvalho Chehab        self.maxDiff = None
298*eea0d807SMauro Carvalho Chehab
299*eea0d807SMauro Carvalho Chehab    def run_out_test(self, fname, source, expected):
300*eea0d807SMauro Carvalho Chehab        """
301*eea0d807SMauro Carvalho Chehab        Generate output using out_style,
302*eea0d807SMauro Carvalho Chehab        """
303*eea0d807SMauro Carvalho Chehab        patcher = patch('builtins.open',
304*eea0d807SMauro Carvalho Chehab                        new_callable=mock_open, read_data=source)
305*eea0d807SMauro Carvalho Chehab
306*eea0d807SMauro Carvalho Chehab        kernel_doc = KernelDoc(self.config, fname, self.xforms)
307*eea0d807SMauro Carvalho Chehab
308*eea0d807SMauro Carvalho Chehab        with patcher:
309*eea0d807SMauro Carvalho Chehab            export_table, entries = kernel_doc.parse_kdoc()
310*eea0d807SMauro Carvalho Chehab
311*eea0d807SMauro Carvalho Chehab        result, expected = gen_output(fname, self.out_style, entries,
312*eea0d807SMauro Carvalho Chehab                                      expected, relax_whitespace=True,
313*eea0d807SMauro Carvalho Chehab                                      config=self.config)
314*eea0d807SMauro Carvalho Chehab
315*eea0d807SMauro Carvalho Chehab        self.assertEqual(result, expected)
316*eea0d807SMauro Carvalho Chehab
317*eea0d807SMauro Carvalho Chehab
318*eea0d807SMauro Carvalho Chehab#
319*eea0d807SMauro Carvalho Chehab# Selftest class
320*eea0d807SMauro Carvalho Chehab#
321*eea0d807SMauro Carvalho Chehabclass TestSelfValidate(GenerateKdocItem):
322*eea0d807SMauro Carvalho Chehab    """
323*eea0d807SMauro Carvalho Chehab    Tests to check if logic inside GenerateKdocItem.run_test() is working.
324a5dea56eSMauro Carvalho Chehab    """
325a5dea56eSMauro Carvalho Chehab
326a5dea56eSMauro Carvalho Chehab    SOURCE = """
327a5dea56eSMauro Carvalho Chehab        /**
328a5dea56eSMauro Carvalho Chehab         * function3: Exported function
329a5dea56eSMauro Carvalho Chehab         * @arg1: @arg1 does nothing
330a5dea56eSMauro Carvalho Chehab         *
331a5dea56eSMauro Carvalho Chehab         * Does nothing
332a5dea56eSMauro Carvalho Chehab         *
333a5dea56eSMauro Carvalho Chehab         * return:
334a5dea56eSMauro Carvalho Chehab         *    always return 0.
335a5dea56eSMauro Carvalho Chehab         */
336a5dea56eSMauro Carvalho Chehab        int function3(char *arg1) { return 0; };
337a5dea56eSMauro Carvalho Chehab        EXPORT_SYMBOL(function3);
338a5dea56eSMauro Carvalho Chehab    """
339a5dea56eSMauro Carvalho Chehab
340a5dea56eSMauro Carvalho Chehab    EXPECTED = [{
341a5dea56eSMauro Carvalho Chehab        'name': 'function3',
342a5dea56eSMauro Carvalho Chehab        'type': 'function',
343a5dea56eSMauro Carvalho Chehab        'declaration_start_line': 2,
344a5dea56eSMauro Carvalho Chehab
345a5dea56eSMauro Carvalho Chehab        'sections_start_lines': {
346a5dea56eSMauro Carvalho Chehab            'Description': 4,
347a5dea56eSMauro Carvalho Chehab            'Return': 7,
348a5dea56eSMauro Carvalho Chehab        },
349a5dea56eSMauro Carvalho Chehab        'sections': {
350a5dea56eSMauro Carvalho Chehab            'Description': 'Does nothing\n\n',
351a5dea56eSMauro Carvalho Chehab            'Return': '\nalways return 0.\n'
352a5dea56eSMauro Carvalho Chehab        },
353*eea0d807SMauro Carvalho Chehab
354*eea0d807SMauro Carvalho Chehab        'sections_start_lines': {
355*eea0d807SMauro Carvalho Chehab            'Description': 4,
356*eea0d807SMauro Carvalho Chehab            'Return': 7,
357*eea0d807SMauro Carvalho Chehab        },
358*eea0d807SMauro Carvalho Chehab
359*eea0d807SMauro Carvalho Chehab        'parameterdescs': {'arg1': '@arg1 does nothing\n'},
360*eea0d807SMauro Carvalho Chehab        'parameterlist': ['arg1'],
361*eea0d807SMauro Carvalho Chehab        'parameterdesc_start_lines': {'arg1': 3},
362*eea0d807SMauro Carvalho Chehab        'parametertypes': {'arg1': 'char *arg1'},
363*eea0d807SMauro Carvalho Chehab
364a5dea56eSMauro Carvalho Chehab        'other_stuff': {
365a5dea56eSMauro Carvalho Chehab            'func_macro': False,
366a5dea56eSMauro Carvalho Chehab            'functiontype': 'int',
367a5dea56eSMauro Carvalho Chehab            'purpose': 'Exported function',
368a5dea56eSMauro Carvalho Chehab            'typedef': False
369a5dea56eSMauro Carvalho Chehab        },
370a5dea56eSMauro Carvalho Chehab    }]
371a5dea56eSMauro Carvalho Chehab
372a5dea56eSMauro Carvalho Chehab    EXPORTS = {"function3"}
373a5dea56eSMauro Carvalho Chehab
374a5dea56eSMauro Carvalho Chehab    def test_parse_pass(self):
375a5dea56eSMauro Carvalho Chehab        """
376a5dea56eSMauro Carvalho Chehab        Test if export_symbol is properly handled.
377a5dea56eSMauro Carvalho Chehab        """
378a5dea56eSMauro Carvalho Chehab        self.run_test(self.SOURCE, self.EXPECTED, self.EXPORTS)
379a5dea56eSMauro Carvalho Chehab
380a5dea56eSMauro Carvalho Chehab    @unittest.expectedFailure
381a5dea56eSMauro Carvalho Chehab    def test_no_exports(self):
382a5dea56eSMauro Carvalho Chehab        """
383a5dea56eSMauro Carvalho Chehab        Test if export_symbol is properly handled.
384a5dea56eSMauro Carvalho Chehab        """
385a5dea56eSMauro Carvalho Chehab        self.run_test(self.SOURCE, [], {})
386a5dea56eSMauro Carvalho Chehab
387a5dea56eSMauro Carvalho Chehab    @unittest.expectedFailure
388a5dea56eSMauro Carvalho Chehab    def test_with_empty_expected(self):
389a5dea56eSMauro Carvalho Chehab        """
390a5dea56eSMauro Carvalho Chehab        Test if export_symbol is properly handled.
391a5dea56eSMauro Carvalho Chehab        """
392a5dea56eSMauro Carvalho Chehab        self.run_test(self.SOURCE, [], self.EXPORTS)
393a5dea56eSMauro Carvalho Chehab
394a5dea56eSMauro Carvalho Chehab    @unittest.expectedFailure
395a5dea56eSMauro Carvalho Chehab    def test_with_unfilled_expected(self):
396a5dea56eSMauro Carvalho Chehab        """
397a5dea56eSMauro Carvalho Chehab        Test if export_symbol is properly handled.
398a5dea56eSMauro Carvalho Chehab        """
399a5dea56eSMauro Carvalho Chehab        self.run_test(self.SOURCE, [{}], self.EXPORTS)
400a5dea56eSMauro Carvalho Chehab
401a5dea56eSMauro Carvalho Chehab    @unittest.expectedFailure
402a5dea56eSMauro Carvalho Chehab    def test_with_default_expected(self):
403a5dea56eSMauro Carvalho Chehab        """
404a5dea56eSMauro Carvalho Chehab        Test if export_symbol is properly handled.
405a5dea56eSMauro Carvalho Chehab        """
406a5dea56eSMauro Carvalho Chehab        self.run_test(self.SOURCE, [self.DEFAULT.copy()], self.EXPORTS)
407a5dea56eSMauro Carvalho Chehab
408a5dea56eSMauro Carvalho Chehab#
409*eea0d807SMauro Carvalho Chehab# Class and logic to create dynamic tests from YAML
410*eea0d807SMauro Carvalho Chehab#
411*eea0d807SMauro Carvalho Chehab
412*eea0d807SMauro Carvalho Chehabclass KernelDocDynamicTests():
413*eea0d807SMauro Carvalho Chehab    """
414*eea0d807SMauro Carvalho Chehab    Dynamically create a set of tests from a YAML file.
415*eea0d807SMauro Carvalho Chehab    """
416*eea0d807SMauro Carvalho Chehab
417*eea0d807SMauro Carvalho Chehab    @classmethod
418*eea0d807SMauro Carvalho Chehab    def create_parser_test(cls, name, fname, source, symbols, exports):
419*eea0d807SMauro Carvalho Chehab        """
420*eea0d807SMauro Carvalho Chehab        Return a function that will be attached to the test class.
421*eea0d807SMauro Carvalho Chehab        """
422*eea0d807SMauro Carvalho Chehab        def test_method(self):
423*eea0d807SMauro Carvalho Chehab            """Lambda-like function to run tests with provided vars"""
424*eea0d807SMauro Carvalho Chehab            self.run_parser_test(source, symbols, exports, fname)
425*eea0d807SMauro Carvalho Chehab
426*eea0d807SMauro Carvalho Chehab        test_method.__name__ = f"test_gen_{name}"
427*eea0d807SMauro Carvalho Chehab
428*eea0d807SMauro Carvalho Chehab        setattr(CToKdocItem, test_method.__name__, test_method)
429*eea0d807SMauro Carvalho Chehab
430*eea0d807SMauro Carvalho Chehab    @classmethod
431*eea0d807SMauro Carvalho Chehab    def create_out_test(cls, name, fname, symbols, out_type, data):
432*eea0d807SMauro Carvalho Chehab        """
433*eea0d807SMauro Carvalho Chehab        Return a function that will be attached to the test class.
434*eea0d807SMauro Carvalho Chehab        """
435*eea0d807SMauro Carvalho Chehab        def test_method(self):
436*eea0d807SMauro Carvalho Chehab            """Lambda-like function to run tests with provided vars"""
437*eea0d807SMauro Carvalho Chehab            self.run_out_test(fname, symbols, data)
438*eea0d807SMauro Carvalho Chehab
439*eea0d807SMauro Carvalho Chehab        test_method.__name__ = f"test_{out_type}_{name}"
440*eea0d807SMauro Carvalho Chehab
441*eea0d807SMauro Carvalho Chehab        if out_type == "man":
442*eea0d807SMauro Carvalho Chehab            setattr(KdocItemToMan, test_method.__name__, test_method)
443*eea0d807SMauro Carvalho Chehab        else:
444*eea0d807SMauro Carvalho Chehab            setattr(KdocItemToRest, test_method.__name__, test_method)
445*eea0d807SMauro Carvalho Chehab
446*eea0d807SMauro Carvalho Chehab    @classmethod
447*eea0d807SMauro Carvalho Chehab    def create_src2out_test(cls, name, fname, source, out_type, data):
448*eea0d807SMauro Carvalho Chehab        """
449*eea0d807SMauro Carvalho Chehab        Return a function that will be attached to the test class.
450*eea0d807SMauro Carvalho Chehab        """
451*eea0d807SMauro Carvalho Chehab        def test_method(self):
452*eea0d807SMauro Carvalho Chehab            """Lambda-like function to run tests with provided vars"""
453*eea0d807SMauro Carvalho Chehab            self.run_out_test(fname, source,  data)
454*eea0d807SMauro Carvalho Chehab
455*eea0d807SMauro Carvalho Chehab        test_method.__name__ = f"test_{out_type}_{name}"
456*eea0d807SMauro Carvalho Chehab
457*eea0d807SMauro Carvalho Chehab        if out_type == "man":
458*eea0d807SMauro Carvalho Chehab            setattr(CToMan, test_method.__name__, test_method)
459*eea0d807SMauro Carvalho Chehab        else:
460*eea0d807SMauro Carvalho Chehab            setattr(CToRest, test_method.__name__, test_method)
461*eea0d807SMauro Carvalho Chehab
462*eea0d807SMauro Carvalho Chehab    @classmethod
463*eea0d807SMauro Carvalho Chehab    def create_tests(cls):
464*eea0d807SMauro Carvalho Chehab        """
465*eea0d807SMauro Carvalho Chehab        Iterate over all scenarios and add a method to the class for each.
466*eea0d807SMauro Carvalho Chehab
467*eea0d807SMauro Carvalho Chehab        The logic in this function assumes a valid test that are compliant
468*eea0d807SMauro Carvalho Chehab        with kdoc-test-schema.yaml. There is an unit test to check that.
469*eea0d807SMauro Carvalho Chehab        As such, it picks mandatory values directly, and uses get() for the
470*eea0d807SMauro Carvalho Chehab        optional ones.
471*eea0d807SMauro Carvalho Chehab        """
472*eea0d807SMauro Carvalho Chehab
473*eea0d807SMauro Carvalho Chehab        with open(TEST_FILE, encoding="utf-8") as fp:
474*eea0d807SMauro Carvalho Chehab            testset = yaml.safe_load(fp)
475*eea0d807SMauro Carvalho Chehab
476*eea0d807SMauro Carvalho Chehab        tests = testset["tests"]
477*eea0d807SMauro Carvalho Chehab
478*eea0d807SMauro Carvalho Chehab        for idx, test in enumerate(tests):
479*eea0d807SMauro Carvalho Chehab            name = test["name"]
480*eea0d807SMauro Carvalho Chehab            fname = test["fname"]
481*eea0d807SMauro Carvalho Chehab            source = test["source"]
482*eea0d807SMauro Carvalho Chehab            expected_list = test["expected"]
483*eea0d807SMauro Carvalho Chehab
484*eea0d807SMauro Carvalho Chehab            exports = test.get("exports", [])
485*eea0d807SMauro Carvalho Chehab
486*eea0d807SMauro Carvalho Chehab            #
487*eea0d807SMauro Carvalho Chehab            # The logic below allows setting up to 5 types of test:
488*eea0d807SMauro Carvalho Chehab            # 1. from source to kdoc_item: test KernelDoc class;
489*eea0d807SMauro Carvalho Chehab            # 2. from kdoc_item to man: test ManOutput class;
490*eea0d807SMauro Carvalho Chehab            # 3. from kdoc_item to rst: test RestOutput class;
491*eea0d807SMauro Carvalho Chehab            # 4. from source to man without checking expected KdocItem;
492*eea0d807SMauro Carvalho Chehab            # 5. from source to rst without checking expected KdocItem.
493*eea0d807SMauro Carvalho Chehab            #
494*eea0d807SMauro Carvalho Chehab            for expected in expected_list:
495*eea0d807SMauro Carvalho Chehab                kdoc_item = expected.get("kdoc_item")
496*eea0d807SMauro Carvalho Chehab                man = expected.get("man", [])
497*eea0d807SMauro Carvalho Chehab                rst = expected.get("rst", [])
498*eea0d807SMauro Carvalho Chehab
499*eea0d807SMauro Carvalho Chehab                if kdoc_item:
500*eea0d807SMauro Carvalho Chehab                    if isinstance(kdoc_item, dict):
501*eea0d807SMauro Carvalho Chehab                        kdoc_item = [kdoc_item]
502*eea0d807SMauro Carvalho Chehab
503*eea0d807SMauro Carvalho Chehab                    symbols = []
504*eea0d807SMauro Carvalho Chehab
505*eea0d807SMauro Carvalho Chehab                    for arg in kdoc_item:
506*eea0d807SMauro Carvalho Chehab                        arg["fname"] = fname
507*eea0d807SMauro Carvalho Chehab                        arg["start_line"] = 1
508*eea0d807SMauro Carvalho Chehab
509*eea0d807SMauro Carvalho Chehab                        symbols.append(KdocItem.from_dict(arg))
510*eea0d807SMauro Carvalho Chehab
511*eea0d807SMauro Carvalho Chehab                    if source:
512*eea0d807SMauro Carvalho Chehab                        cls.create_parser_test(name, fname, source,
513*eea0d807SMauro Carvalho Chehab                                               symbols, exports)
514*eea0d807SMauro Carvalho Chehab
515*eea0d807SMauro Carvalho Chehab                    if man:
516*eea0d807SMauro Carvalho Chehab                        cls.create_out_test(name, fname, symbols, "man", man)
517*eea0d807SMauro Carvalho Chehab
518*eea0d807SMauro Carvalho Chehab                    if rst:
519*eea0d807SMauro Carvalho Chehab                        cls.create_out_test(name, fname, symbols, "rst", rst)
520*eea0d807SMauro Carvalho Chehab
521*eea0d807SMauro Carvalho Chehab                elif source:
522*eea0d807SMauro Carvalho Chehab                    if man:
523*eea0d807SMauro Carvalho Chehab                        cls.create_src2out_test(name, fname, source, "man", man)
524*eea0d807SMauro Carvalho Chehab
525*eea0d807SMauro Carvalho Chehab                    if rst:
526*eea0d807SMauro Carvalho Chehab                        cls.create_src2out_test(name, fname, source, "rst", rst)
527*eea0d807SMauro Carvalho Chehab
528*eea0d807SMauro Carvalho ChehabKernelDocDynamicTests.create_tests()
529*eea0d807SMauro Carvalho Chehab
530*eea0d807SMauro Carvalho Chehab#
531a5dea56eSMauro Carvalho Chehab# Run all tests
532a5dea56eSMauro Carvalho Chehab#
533a5dea56eSMauro Carvalho Chehabif __name__ == "__main__":
534a5dea56eSMauro Carvalho Chehab    run_unittest(__file__)
535