Skip to content

Commit d88d659

Browse files
committed
Added initial support for external package addition.
1 parent 4ce63ad commit d88d659

File tree

5 files changed

+653
-1
lines changed

5 files changed

+653
-1
lines changed

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ classifiers = [
3636
requires-python = ">=3.10"
3737
dependencies = [
3838
"colorama",
39+
"tomli; python_version < '3.11'",
3940
]
4041

4142
[tool.setuptools]

tests/unit/test_builtins.py

Lines changed: 346 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,354 @@
88
Test builtins.py
99
"""
1010

11+
from tempfile import tempdir
1112
import unittest
13+
import re
1214
from unittest import mock
13-
from vunit.builtins import BuiltinsAdder
15+
from vunit import VUnit
16+
from vunit.builtins import Builtins, BuiltinsAdder
17+
from vunit.about import version
18+
from vunit.vhdl_standard import VHDLStandard
19+
from vunit.project import Project
20+
from tests.common import create_tempdir
21+
from contextlib import contextmanager
22+
23+
24+
@contextmanager
25+
def pkg_env(tempdir):
26+
with (
27+
mock.patch("vunit.builtins.importlib.util.find_spec"),
28+
mock.patch("vunit.builtins.importlib.resources.files", return_value=tempdir),
29+
):
30+
yield
31+
32+
33+
class TestBuiltins(unittest.TestCase):
34+
"""
35+
Test Builtins class
36+
"""
37+
38+
def setUp(self):
39+
self.vu = mock.create_autospec(VUnit, instance=True)
40+
self.vu._project = mock.create_autospec(Project, instance=True)
41+
self.vu._project._libraries = []
42+
self.library_mock = mock.Mock()
43+
44+
def add_library(name):
45+
self.vu._project._libraries.append(name)
46+
return self.library_mock
47+
48+
self.vu.add_library.side_effect = add_library
49+
50+
self.builtins = Builtins(self.vu, VHDLStandard("2002"), None)
51+
52+
def _assertLogContent(self, log, level, msg):
53+
self.assertEqual(len(log.records), 1)
54+
record = log.records[0]
55+
self.assertEqual(record.levelname, level)
56+
self.assertEqual(record.getMessage(), msg)
57+
58+
def _write_toml(self, tempdir, text: str):
59+
(tempdir / "vunit_pkg.toml").write_text(text, encoding="utf-8")
60+
61+
def test_raises_if_package_not_found(self):
62+
with (
63+
mock.patch("vunit.builtins.importlib.util.find_spec", return_value=None),
64+
self.assertRaisesRegex(RuntimeError, re.escape("Could not find package foo.")),
65+
):
66+
self.builtins.add_package("foo")
67+
68+
def test_normalizes_package_name(self):
69+
with (
70+
mock.patch("vunit.builtins.importlib.util.find_spec", return_value=None),
71+
self.assertRaisesRegex(RuntimeError, re.escape("Could not find package a_b_c_d.")),
72+
):
73+
self.builtins.add_package("a-b.c-d")
74+
75+
def test_raises_if_toml_not_in_package(self):
76+
with (
77+
create_tempdir() as tempdir,
78+
pkg_env(tempdir),
79+
self.assertRaisesRegex(
80+
RuntimeError, re.escape(f"Could not find vunit_pkg.toml for package foo in {tempdir}.")
81+
),
82+
):
83+
(tempdir / "not_vunit_pkg.toml").write_text("")
84+
self.builtins.add_package("foo")
85+
86+
def test_raises_if_invalid_toml_format(self):
87+
with (
88+
create_tempdir() as tempdir,
89+
pkg_env(tempdir),
90+
self.assertRaisesRegex(RuntimeError, re.escape("vunit_pkg.toml for package foo is not a valid TOML file")),
91+
):
92+
self._write_toml(
93+
tempdir,
94+
"""\
95+
[package
96+
""",
97+
)
98+
self.builtins.add_package("foo")
99+
100+
def test_raises_if_missing_package(self):
101+
with (
102+
create_tempdir() as tempdir,
103+
pkg_env(tempdir),
104+
self.assertRaisesRegex(RuntimeError, re.escape("Invalid vunit_pkg.toml: 2 error(s) found.")),
105+
):
106+
self._write_toml(
107+
tempdir,
108+
"""\
109+
[pkg]
110+
""",
111+
)
112+
self.builtins.add_package("foo")
113+
114+
def test_accepts_empty_toml_package(self):
115+
with (
116+
create_tempdir() as tempdir,
117+
pkg_env(tempdir),
118+
):
119+
self._write_toml(
120+
tempdir,
121+
"""\
122+
[package]
123+
""",
124+
)
125+
self.builtins.add_package("foo")
126+
127+
def test_raises_if_incompatible_vunit_version(self):
128+
with (
129+
create_tempdir() as tempdir,
130+
pkg_env(tempdir),
131+
self.assertRaisesRegex(
132+
RuntimeError,
133+
re.escape(f"Package foo requires VUnit version ==1000.0.0 but current version is {version()}."),
134+
),
135+
):
136+
self._write_toml(
137+
tempdir,
138+
"""\
139+
[package]
140+
requires-vunit="==1000.0.0"
141+
""",
142+
)
143+
self.builtins.add_package("foo")
144+
145+
def test_warns_if_multi_vhdl_standard(self):
146+
with (
147+
create_tempdir() as tempdir,
148+
pkg_env(tempdir),
149+
self.assertLogs("vunit.builtins", "WARNING") as mock_warning,
150+
):
151+
self._write_toml(
152+
tempdir,
153+
"""\
154+
[package]
155+
requires-vhdl=">=2008"
156+
""",
157+
)
158+
self.builtins.add_package("foo")
159+
160+
self._assertLogContent(
161+
mock_warning,
162+
"WARNING",
163+
"Package foo requires VHDL standard >=2008 but current standard is 2002. "
164+
"Proceeding with mixed-language compilation using VHDL standard 2008 for the package.",
165+
)
166+
167+
def test_raises_if_no_compatible_vhdl_standard(self):
168+
with (
169+
create_tempdir() as tempdir,
170+
pkg_env(tempdir),
171+
self.assertRaisesRegex(
172+
RuntimeError,
173+
re.escape("Package foo requires VHDL standard <2008,>2008. Failed to find a compatible standard."),
174+
),
175+
):
176+
self._write_toml(
177+
tempdir,
178+
"""\
179+
[package]
180+
requires-vhdl="<2008,>2008"
181+
""",
182+
)
183+
self.builtins.add_package("foo")
184+
185+
def test_accepts_compatible_vhdl_standard(self):
186+
with (
187+
create_tempdir() as tempdir,
188+
pkg_env(tempdir),
189+
):
190+
self._write_toml(
191+
tempdir,
192+
"""\
193+
[package]
194+
requires-vhdl=">1993"
195+
""",
196+
)
197+
self.builtins.add_package("foo")
198+
199+
def test_raises_if_unsupported_version_specifier_operator(self):
200+
with (
201+
create_tempdir() as tempdir,
202+
pkg_env(tempdir),
203+
self.assertRaisesRegex(
204+
RuntimeError,
205+
re.escape("Unsupported version specifier operator: ===."),
206+
),
207+
):
208+
self._write_toml(
209+
tempdir,
210+
"""\
211+
[package]
212+
requires-vhdl="===2008"
213+
""",
214+
)
215+
self.builtins.add_package("foo")
216+
217+
def test_raises_if_invalid_version_specifier_operator(self):
218+
with (
219+
create_tempdir() as tempdir,
220+
pkg_env(tempdir),
221+
self.assertRaisesRegex(
222+
RuntimeError,
223+
re.escape(
224+
"Invalid version requirement format: =!2008. Use <OPERATOR><VERSION> where "
225+
"OPERATOR is one of <=, <, !=, ==, >=, and >."
226+
),
227+
),
228+
):
229+
self._write_toml(
230+
tempdir,
231+
"""\
232+
[package]
233+
requires-vhdl="=!2008"
234+
""",
235+
)
236+
self.builtins.add_package("foo")
237+
238+
def test_raises_if_invalid_key(self):
239+
with (
240+
create_tempdir() as tempdir,
241+
pkg_env(tempdir),
242+
self.assertRaisesRegex(
243+
RuntimeError,
244+
re.escape("Invalid vunit_pkg.toml: 1 error(s) found."),
245+
),
246+
self.assertLogs("vunit.builtins", "ERROR") as mock_error,
247+
):
248+
self._write_toml(
249+
tempdir,
250+
"""\
251+
[package]
252+
require-vhdl="<1993"
253+
""",
254+
)
255+
self.builtins.add_package("foo")
256+
257+
self._assertLogContent(
258+
mock_error,
259+
"ERROR",
260+
"package.require-vhdl: Unexpected string 'require-vhdl'.",
261+
)
262+
263+
def test_raises_if_invalid_type(self):
264+
with (
265+
create_tempdir() as tempdir,
266+
pkg_env(tempdir),
267+
self.assertRaisesRegex(
268+
RuntimeError,
269+
re.escape("Invalid vunit_pkg.toml: 1 error(s) found."),
270+
),
271+
self.assertLogs("vunit.builtins", "ERROR") as mock_error,
272+
):
273+
self._write_toml(
274+
tempdir,
275+
"""\
276+
[package]
277+
requires-vhdl=2002
278+
""",
279+
)
280+
self.builtins.add_package("foo")
281+
282+
self._assertLogContent(
283+
mock_error,
284+
"ERROR",
285+
"package.requires-vhdl: 'requires-vhdl' must be a string.",
286+
)
287+
288+
def test_adding_package_to_library(self):
289+
with (
290+
create_tempdir() as tempdir,
291+
pkg_env(tempdir),
292+
):
293+
self._write_toml(
294+
tempdir,
295+
"""\
296+
[package]
297+
requires-vhdl="<=2008"
298+
library = "bar"
299+
[[package.sources]]
300+
include=["hdl/src1/*.vhd", "hdl/src2/*.vhd"]
301+
[[package.sources]]
302+
include=["hdl/src3/*.vhd"]
303+
""",
304+
)
305+
306+
self.builtins.add_package("foo")
307+
308+
self.assertIn("bar", self.vu._project._libraries)
309+
self.library_mock.add_source_files.assert_has_calls(
310+
[
311+
mock.call(tempdir / "hdl/src1/*.vhd", vhdl_standard=None),
312+
mock.call(tempdir / "hdl/src2/*.vhd", vhdl_standard=None),
313+
mock.call(tempdir / "hdl/src3/*.vhd", vhdl_standard=None),
314+
]
315+
)
316+
317+
def test_raises_if_missing_library(self):
318+
with (
319+
create_tempdir() as tempdir,
320+
pkg_env(tempdir),
321+
self.assertRaisesRegex(RuntimeError, re.escape("Invalid vunit_pkg.toml: 1 error(s) found.")),
322+
):
323+
self._write_toml(
324+
tempdir,
325+
"""\
326+
[package]
327+
[[package.sources]]
328+
include=["hdl/src1/*.vhd"]
329+
""",
330+
)
331+
self.builtins.add_package("foo")
332+
333+
def test_warns_if_package_already_added(self):
334+
with (
335+
create_tempdir() as tempdir,
336+
pkg_env(tempdir),
337+
self.assertLogs("vunit.builtins", "WARNING") as mock_warning,
338+
):
339+
self._write_toml(
340+
tempdir,
341+
"""\
342+
[package]
343+
requires-vhdl="!=93"
344+
library = "bar"
345+
[[package.sources]]
346+
include=["hdl/src1/*.vhd"]
347+
""",
348+
)
349+
self.builtins.add_package("foo")
350+
self.library_mock.add_source_files.assert_called_once_with(tempdir / "hdl/src1/*.vhd", vhdl_standard=None)
351+
self.builtins.add_package("foo")
352+
353+
self._assertLogContent(
354+
mock_warning,
355+
"WARNING",
356+
"Library bar previously defined. Skipping addition of foo.",
357+
)
358+
self.library_mock.add_source_files.assert_called_once()
14359

15360

16361
class TestBuiltinsAdder(unittest.TestCase):

0 commit comments

Comments
 (0)