1from pathlib import Path
2
3import pytest
4
5from genpinctrl import (
6    validate_config_entry,
7    format_mode,
8    format_mode_f1,
9    format_remap,
10    format_remap_name,
11    get_gpio_ip_afs,
12    get_mcu_signals,
13    main,
14)
15
16
17def test_validate_config_entry():
18    """Check that config entries are validated correctly"""
19
20    # no name
21    entry = {}
22    with pytest.raises(ValueError):
23        validate_config_entry(entry, "")
24
25    # no match
26    entry = {"name": "TEST"}
27    with pytest.raises(ValueError):
28        validate_config_entry(entry, "")
29
30    # no mode: F1 error only
31    entry = {"name": "TEST", "match": "TEST"}
32    with pytest.raises(ValueError):
33        validate_config_entry(entry, "STM32F1")
34
35    # invalid mode
36    entry = {"name": "TEST", "match": "TEST"}
37    entry["mode"] = "INVALID"
38    with pytest.raises(ValueError):
39        validate_config_entry(entry, "STM32F0")
40    with pytest.raises(ValueError):
41        validate_config_entry(entry, "STM32F1")
42
43    # valid modes
44    entry = {"name": "TEST", "match": "TEST"}
45    entry["mode"] = "analog"
46    validate_config_entry(entry, "STM32F0")
47    validate_config_entry(entry, "STM32F1")
48    entry["mode"] = "alternate"
49    validate_config_entry(entry, "STM32F0")
50    validate_config_entry(entry, "STM32F1")
51    entry["mode"] = "input"
52    validate_config_entry(entry, "STM32F1")
53
54    # invalid bias
55    entry = {"name": "TEST", "match": "TEST", "mode": "alternate"}
56    entry["bias"] = "INVALID"
57    with pytest.raises(ValueError):
58        validate_config_entry(entry, "STM32F0")
59
60    entry["bias"] = "pull-up"
61    with pytest.raises(ValueError):
62        validate_config_entry(entry, "STM32F1")
63
64    # valid bias
65    entry = {"name": "TEST", "match": "TEST"}
66    entry["bias"] = "disable"
67    entry["mode"] = "alternate"
68    validate_config_entry(entry, "STM32F0")
69    validate_config_entry(entry, "STM32F1")
70    entry["mode"] = "input"
71    validate_config_entry(entry, "STM32F1")
72
73    entry["bias"] = "pull-up"
74    entry["mode"] = "alternate"
75    validate_config_entry(entry, "STM32F0")
76    entry["mode"] = "input"
77    validate_config_entry(entry, "STM32F1")
78
79    entry["bias"] = "pull-down"
80    entry["mode"] = "alternate"
81    validate_config_entry(entry, "STM32F0")
82    entry["mode"] = "input"
83    validate_config_entry(entry, "STM32F1")
84
85    # invalid drive
86    entry = {"name": "TEST", "match": "TEST", "mode": "alternate"}
87    entry["drive"] = "INVALID"
88    with pytest.raises(ValueError):
89        validate_config_entry(entry, "STM32F0")
90
91    # valid drive
92    entry = {"name": "TEST", "match": "TEST", "mode": "alternate"}
93    entry["drive"] = "push-pull"
94    validate_config_entry(entry, "STM32F0")
95    entry["bias"] = "open-drain"
96
97    # invalid slew-rate
98    entry = {"name": "TEST", "match": "TEST", "mode": "alternate"}
99    entry["slew-rate"] = "INVALID"
100    with pytest.raises(ValueError):
101        validate_config_entry(entry, "STM32F0")
102    with pytest.raises(ValueError):
103        validate_config_entry(entry, "STM32F1")
104
105    # valid slew-rate
106    entry = {"name": "TEST", "match": "TEST", "mode": "alternate"}
107    entry["slew-rate"] = "low-speed"
108    validate_config_entry(entry, "STM32F0")
109    entry["slew-rate"] = "medium-speed"
110    validate_config_entry(entry, "STM32F0")
111    entry["slew-rate"] = "high-speed"
112    validate_config_entry(entry, "STM32F0")
113    entry["slew-rate"] = "very-high-speed"
114    validate_config_entry(entry, "STM32F0")
115
116    entry["slew-rate"] = "max-speed-10mhz"
117    validate_config_entry(entry, "STM32F1")
118    entry["slew-rate"] = "max-speed-2mhz"
119    validate_config_entry(entry, "STM32F1")
120    entry["slew-rate"] = "max-speed-50mhz"
121    validate_config_entry(entry, "STM32F1")
122
123
124def test_format_mode():
125    """Test that format_mode works."""
126
127    assert format_mode("analog", None) == "ANALOG"
128    assert format_mode("alternate", 0) == "AF0"
129
130    with pytest.raises(ValueError):
131        format_mode("INVALID", 0)
132
133
134def test_format_mode_f1():
135    """Test that format_mode_f1 works."""
136
137    assert format_mode_f1("analog") == "ANALOG"
138    assert format_mode_f1("input") == "GPIO_IN"
139    assert format_mode_f1("alternate") == "ALTERNATE"
140
141    with pytest.raises(ValueError):
142        format_mode_f1("INVALID")
143
144
145def test_format_remap():
146    """Test that format_remap works."""
147
148    assert format_remap("UART1_REMAP1") == "UART1_REMAP1"
149    assert format_remap(0) == "NO_REMAP"
150    assert format_remap(None) == "NO_REMAP"
151
152
153def test_format_remap_name():
154    """Test that format_remap_name works."""
155
156    assert format_remap_name("UART1_REMAP3") == "_remap3"
157    assert format_remap_name("UART1_REMAP2") == "_remap2"
158    assert format_remap_name("UART1_REMAP1") == "_remap1"
159    assert format_remap_name("UART1_REMAP0") == ""
160    assert format_remap_name(None) == ""
161
162
163def test_get_gpio_ip_afs(pindata):
164    """Test that IP AF files part of test STM32 Open Pin Data files are parsed
165       correctly.
166    """
167
168    afs = get_gpio_ip_afs(pindata)
169    assert afs == {
170        "STM32F0TESTIP": {
171            "PA0": {
172                "UART1_TX": 0,
173                "UART1_RX": 1,
174            }
175        },
176        "STM32F1TESTIP": {
177            "PA0": {
178                "UART1_TX": ["UART1_REMAP0",
179                             "UART1_REMAP1",
180                             "UART1_REMAP2",
181                             "UART1_REMAP3"],
182                "UART1_RX": ["UART1_REMAP1"],
183            }
184        },
185    }
186
187
188def test_get_mcu_signals(pindata):
189    """Test that MCU files part of test STM32 Open Pin Data are parsed correctly."""
190
191    afs = get_gpio_ip_afs(pindata)
192    signals = get_mcu_signals(pindata, afs)
193    assert signals == {
194        "STM32F0": [
195            {
196                "name": "STM32F0TESTDIE",
197                "pins": [
198                    {
199                        "port": "a",
200                        "pin": 0,
201                        "mod": "",
202                        "signals": [
203                            {"name": "UART1_TX", "af": 0},
204                            {"name": "UART1_RX", "af": 1},
205                            {"name": "ADC1_IN0", "af": None},
206                        ],
207                    },
208                ],
209            },
210        ],
211        "STM32F1": [
212            {
213                "name": "STM32F1TESTDIE",
214                "pins": [
215                    {
216                        "port": "a",
217                        "pin": 0,
218                        "mod": "",
219                        "signals": [
220                            {"name": "UART1_TX", "af": "UART1_REMAP0"},
221                            {"name": "UART1_TX", "af": "UART1_REMAP1"},
222                            {"name": "UART1_TX", "af": "UART1_REMAP2"},
223                            {"name": "UART1_TX", "af": "UART1_REMAP3"},
224                            {"name": "UART1_RX", "af": "UART1_REMAP1"},
225                            {"name": "ADC1_IN0", "af": 0},
226                            {"name": "I2C2_SCL", "af": 0},
227                        ],
228                    },
229                ],
230            },
231        ],
232    }
233
234
235def test_folders_missing():
236    """Test that missing STM32 Open Pin Data folders is handled correctly by
237       parsing functions.
238    """
239
240    with pytest.raises(FileNotFoundError):
241        get_gpio_ip_afs(Path("MISSING_PATH"))
242
243    with pytest.raises(FileNotFoundError):
244        get_mcu_signals(Path("MISSING_PATH"), dict())
245
246
247def test_main(data, pindata, tmp_path, mocker):
248    """Test that DTS files are generated correctly."""
249
250    mocker.patch("genpinctrl.check_output", return_value=b"TEST")
251
252    main(pindata, tmp_path)
253
254    # check readme file
255    ref_readme_file = data / "README.rst"
256    gen_readme_file = tmp_path / "README.rst"
257
258    assert gen_readme_file.exists()
259
260    with open(ref_readme_file) as ref, open(gen_readme_file) as gen:
261        assert ref.read() == gen.read()
262
263    # check f0 file
264    ref_pinctrl_file = data / "stm32f0testdie-pinctrl.dtsi"
265    gen_pinctrl_file = tmp_path / "st" / "f0" / "stm32f0testdie-pinctrl.dtsi"
266
267    assert gen_pinctrl_file.exists()
268
269    with open(ref_pinctrl_file) as ref, open(gen_pinctrl_file) as gen:
270        assert ref.read() == gen.read()
271
272    # check f1 file
273    ref_pinctrl_file = data / "stm32f1testdie-pinctrl.dtsi"
274    gen_pinctrl_file = tmp_path / "st" / "f1" / "stm32f1testdie-pinctrl.dtsi"
275
276    assert gen_pinctrl_file.exists()
277
278    with open(ref_pinctrl_file) as ref, open(gen_pinctrl_file) as gen:
279        assert ref.read() == gen.read()
280