Coverage for tsfpga/yosys/test/test_common.py: 100%
91 statements
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-09 00:40 +0000
« prev ^ index » next coverage.py v7.16.2, created at 2026-10-09 00:40 +0000
1# --------------------------------------------------------------------------------------------------
2# Copyright (c) Lukas Vik. All rights reserved.
3#
4# This file is part of the tsfpga project, a project platform for modern FPGA development.
5# https://tsfpga.com
6# https://github.com/tsfpga/tsfpga
7# --------------------------------------------------------------------------------------------------
9import subprocess
10from pathlib import Path
11from unittest.mock import patch
13import pytest
15from tsfpga.yosys.common import (
16 get_ghdl_library_prefix,
17 get_ghdl_path,
18 get_yosys_path,
19 run_ghdl,
20 run_yosys,
21 to_yosys_path,
22)
24THIS_DIR = Path(__file__).parent
27def test_run_ghdl():
28 ghdl_path = THIS_DIR / "ghdl.exe"
29 arguments = ["-a", "--std=08", "apa.vhd"]
30 expected_cmd = [str(ghdl_path.resolve()), *arguments]
32 with patch("tsfpga.yosys.common.Process") as mocked_process:
33 mocked_process.NonZeroExitCode = ValueError
34 assert run_ghdl(ghdl_path=ghdl_path, arguments=arguments, cwd=THIS_DIR)
35 mocked_process.assert_called_once_with(args=expected_cmd, cwd=THIS_DIR)
37 with patch("tsfpga.yosys.common.Process") as mocked_process:
38 mocked_process.NonZeroExitCode = ValueError
39 mocked_process.return_value.consume_output.side_effect = ValueError("Non-zero exit code!")
40 assert not run_ghdl(ghdl_path=ghdl_path, arguments=arguments, cwd=THIS_DIR)
43def test_run_yosys():
44 yosys_path = THIS_DIR / "yosys.exe"
45 ghdl_plugin_path = THIS_DIR / "ghdl.so"
46 script_file = THIS_DIR / "apa.ys"
47 expected_cmd = [
48 str(yosys_path.resolve()),
49 "-m",
50 str(ghdl_plugin_path.resolve()),
51 "-s",
52 str(script_file.resolve()),
53 ]
55 with (
56 patch("tsfpga.yosys.common.Process") as mocked_process,
57 patch("tsfpga.yosys.common.get_ghdl_library_prefix", return_value=None),
58 ):
59 mocked_process.NonZeroExitCode = ValueError
60 assert run_yosys(
61 yosys_path=yosys_path,
62 ghdl_plugin_path=ghdl_plugin_path,
63 script_file=script_file,
64 cwd=THIS_DIR,
65 )
66 mocked_process.assert_called_once_with(args=expected_cmd, cwd=THIS_DIR, env=None)
68 with (
69 patch("tsfpga.yosys.common.Process") as mocked_process,
70 patch("tsfpga.yosys.common.get_ghdl_library_prefix", return_value=None),
71 ):
72 mocked_process.NonZeroExitCode = ValueError
73 mocked_process.return_value.consume_output.side_effect = ValueError("Non-zero exit code!")
74 assert not run_yosys(
75 yosys_path=yosys_path,
76 ghdl_plugin_path=None,
77 script_file=script_file,
78 cwd=THIS_DIR,
79 )
82def test_run_yosys_without_plugin_does_not_add_dash_m_argument():
83 yosys_path = THIS_DIR / "yosys.exe"
84 script_file = THIS_DIR / "apa.ys"
85 expected_cmd = [str(yosys_path.resolve()), "-s", str(script_file.resolve())]
87 with (
88 patch("tsfpga.yosys.common.Process") as mocked_process,
89 patch("tsfpga.yosys.common.get_ghdl_library_prefix", return_value=None),
90 ):
91 mocked_process.NonZeroExitCode = ValueError
92 assert run_yosys(
93 yosys_path=yosys_path, ghdl_plugin_path=None, script_file=script_file, cwd=THIS_DIR
94 )
95 mocked_process.assert_called_once_with(args=expected_cmd, cwd=THIS_DIR, env=None)
98def test_run_yosys_sets_ghdl_prefix_environment_variable():
99 yosys_path = THIS_DIR / "yosys.exe"
100 script_file = THIS_DIR / "apa.ys"
101 ghdl_prefix = THIS_DIR / "ghdl_libs"
103 with (
104 patch("tsfpga.yosys.common.Process") as mocked_process,
105 patch("tsfpga.yosys.common.get_ghdl_library_prefix") as mocked_get_prefix,
106 ):
107 mocked_process.NonZeroExitCode = ValueError
108 assert run_yosys(
109 yosys_path=yosys_path,
110 ghdl_plugin_path=None,
111 script_file=script_file,
112 cwd=THIS_DIR,
113 ghdl_prefix=ghdl_prefix,
114 )
115 _, kwargs = mocked_process.call_args
116 assert kwargs["env"]["GHDL_PREFIX"] == str(ghdl_prefix.resolve())
117 # Explicit argument shall take precedence: auto-detection shall not even be attempted.
118 mocked_get_prefix.assert_not_called()
121def test_run_yosys_auto_detects_ghdl_prefix_environment_variable():
122 yosys_path = THIS_DIR / "yosys.exe"
123 ghdl_path = THIS_DIR / "ghdl.exe"
124 script_file = THIS_DIR / "apa.ys"
125 ghdl_prefix = THIS_DIR / "ghdl_libs"
127 with (
128 patch("tsfpga.yosys.common.Process") as mocked_process,
129 patch(
130 "tsfpga.yosys.common.get_ghdl_library_prefix", return_value=ghdl_prefix
131 ) as mocked_get_prefix,
132 ):
133 mocked_process.NonZeroExitCode = ValueError
134 assert run_yosys(
135 yosys_path=yosys_path,
136 ghdl_plugin_path=None,
137 script_file=script_file,
138 cwd=THIS_DIR,
139 ghdl_path=ghdl_path,
140 )
141 mocked_get_prefix.assert_called_once_with(ghdl_path)
142 _, kwargs = mocked_process.call_args
143 assert kwargs["env"]["GHDL_PREFIX"] == str(ghdl_prefix.resolve())
146def test_get_ghdl_library_prefix():
147 ghdl_path = THIS_DIR / "ghdl.exe"
148 completed_process = subprocess.CompletedProcess(
149 args=[],
150 returncode=0,
151 stdout=(
152 "command line prefix (--PREFIX): (not set)\n"
153 "environment prefix (GHDL_PREFIX): (not set)\n"
154 "exec prefix (from program name): /apa/ghdl\n"
155 "library prefix: /apa/ghdl/lib/ghdl\n"
156 ),
157 )
158 with patch("tsfpga.yosys.common.subprocess.run", return_value=completed_process):
159 assert get_ghdl_library_prefix(ghdl_path) == Path("/apa/ghdl/lib/ghdl")
162def test_get_ghdl_library_prefix_returns_none_if_command_fails():
163 ghdl_path = THIS_DIR / "ghdl.exe"
164 with patch(
165 "tsfpga.yosys.common.subprocess.run",
166 side_effect=subprocess.CalledProcessError(1, []),
167 ):
168 assert get_ghdl_library_prefix(ghdl_path) is None
171def test_get_ghdl_library_prefix_returns_none_if_ghdl_is_not_found():
172 with patch("tsfpga.yosys.common.which", return_value=None):
173 assert get_ghdl_library_prefix() is None
176def test_get_ghdl_library_prefix_returns_none_if_output_can_not_be_parsed():
177 ghdl_path = THIS_DIR / "ghdl.exe"
178 completed_process = subprocess.CompletedProcess(args=[], returncode=0, stdout="apa\n")
179 with patch("tsfpga.yosys.common.subprocess.run", return_value=completed_process):
180 assert get_ghdl_library_prefix(ghdl_path) is None
183def test_get_ghdl_path_raises_exception_if_not_found_in_path():
184 with (
185 patch("tsfpga.yosys.common.which", return_value=None),
186 pytest.raises(FileNotFoundError, match="Could not find ghdl on PATH"),
187 ):
188 get_ghdl_path()
191def test_get_yosys_path_raises_exception_if_not_found_in_path():
192 with (
193 patch("tsfpga.yosys.common.which", return_value=None),
194 pytest.raises(FileNotFoundError, match="Could not find yosys on PATH"),
195 ):
196 get_yosys_path()
199def test_get_ghdl_path_with_explicit_path_does_not_check_path_env_variable():
200 ghdl_path = THIS_DIR / "ghdl.exe"
201 with patch("tsfpga.yosys.common.which") as mocked_which:
202 assert get_ghdl_path(ghdl_path) == ghdl_path.resolve()
203 mocked_which.assert_not_called()
206def test_to_yosys_path():
207 expected = (THIS_DIR / "apa.vhd").resolve().as_posix()
208 assert to_yosys_path(THIS_DIR / "apa.vhd") == expected