xref: /illumos-gate/usr/src/test/header-tests/tests/common/test_parse_sym_cfg.py (revision e1e6b944360d951edf36ef4198261818ed2d9b2f)
1#!/usr/bin/python3
2#
3# This file and its contents are supplied under the terms of the
4# Common Development and Distribution License ("CDDL"), version 1.0.
5# You may only use this file in accordance with the terms of version
6# 1.0 of the CDDL.
7#
8# A full copy of the text of the CDDL should have accompanied this
9# source.  A copy of the CDDL is also available via the Internet at
10# http://www.illumos.org/license/CDDL.
11#
12
13#
14# Copyright 2026 Gordon W. Ross
15#
16
17"""
18Unit tests for SymConfig._parse() - the symbols config file parser.
19
20Tests use canned string input via io.StringIO so no external files are needed.
21"""
22
23import contextlib
24import io
25import unittest
26
27from symbol_test import SymConfig, SymEntry
28
29# ---------------------------------------------------------------------------
30# Canned input: exercises all four directive types, continuation lines,
31# multiple headers (;-separated), multiple arg types (;-separated),
32# comments, and blank lines.
33# ---------------------------------------------------------------------------
34
35CANNED_SYM = """\
36#
37# Canned symbol test cfg for unit testing.
38#
39
40# A type test.
41type | size_t | test.h | C11+
42
43# A value test.
44value | M_PI | double | test.h | C99+
45
46# A define test with no expected value.
47define | INFINITY | | test.h | C99+
48
49# A define test with an expected value.
50define | FLT_RADIX | 2 | test.h | C99+
51
52# A simple func test (single arg, single header).
53func | log | double | double | test.h | C99+
54
55# A func test with continuation lines and multiple args.
56func | hypot			|\
57	double				|\
58	double; double			|\
59	test.h | C99+
60
61# A func test with multiple headers (;-separated).
62func | acosh			|\
63	double				|\
64	double				|\
65	test.h; helper.h | C99+
66"""
67
68# ---------------------------------------------------------------------------
69# Expected results
70# ---------------------------------------------------------------------------
71
72EXPECTED = [
73    dict(directive='type',   symbol='size_t',   rtype='size_t',
74         atypes=[],                headers=['test.h'],                env_spec='C11+'),
75    dict(directive='value',  symbol='M_PI',     rtype='double',
76         atypes=[],                headers=['test.h'],                env_spec='C99+'),
77    dict(directive='define', symbol='INFINITY',  rtype=None,
78         atypes=[],                headers=['test.h'],   defval=None, env_spec='C99+'),
79    dict(directive='define', symbol='FLT_RADIX', rtype=None,
80         atypes=[],                headers=['test.h'],   defval='2',  env_spec='C99+'),
81    dict(directive='func',   symbol='log',       rtype='double',
82         atypes=['double'],        headers=['test.h'],                env_spec='C99+'),
83    dict(directive='func',   symbol='hypot',     rtype='double',
84         atypes=['double', 'double'], headers=['test.h'],             env_spec='C99+'),
85    dict(directive='func',   symbol='acosh',     rtype='double',
86         atypes=['double'],        headers=['test.h', 'helper.h'],     env_spec='C99+'),
87]
88
89
90class TestParseSymCfg(unittest.TestCase):
91
92    def setUp(self):
93        self.cfg = SymConfig()
94        self.cfg._parse(io.StringIO(CANNED_SYM), filename='<test>')
95
96    def test_entry_count(self):
97        self.assertEqual(len(self.cfg.entries), len(EXPECTED))
98
99    def test_primary_header(self):
100        self.assertEqual(self.cfg.primary_header, 'test.h')
101
102    def test_entries(self):
103        for i, exp in enumerate(EXPECTED):
104            with self.subTest(i=i, symbol=exp['symbol']):
105                e = self.cfg.entries[i]
106                self.assertEqual(e.directive, exp['directive'])
107                self.assertEqual(e.symbol,    exp['symbol'])
108                self.assertEqual(e.env_spec,  exp['env_spec'])
109                self.assertEqual(e.headers,   exp['headers'])
110                self.assertEqual(e.atypes,    exp['atypes'])
111                if exp['directive'] == 'define':
112                    self.assertEqual(e.defval, exp.get('defval'))
113                else:
114                    self.assertEqual(e.rtype,  exp['rtype'])
115
116    def test_primary_header_mismatch(self):
117        text = (
118            'type | int | first.h | ALL\n'
119            'type | int | second.h | ALL\n'
120        )
121        cfg = SymConfig()
122        err = io.StringIO()
123        with contextlib.redirect_stderr(err):
124            cfg._parse(io.StringIO(text), filename='one.cfg')
125        self.assertIn('Only one primary header per configuration file',
126                      err.getvalue())
127        self.assertNotEqual(cfg.entries[1].headers[0], cfg.primary_header)
128
129        with self.assertRaises(SystemExit):
130            cfg._parse(io.StringIO('type | int | other.h | ALL\n'),
131                       filename='two.cfg')
132
133if __name__ == '__main__':
134    unittest.main()
135