Skip to content

Commit 2b45423

Browse files
committed
Add comprehensive test suite for improved code coverage
- Add tests for SiliconStep and SiliconTop in test_steps_silicon.py - Add test_buffers.py for buffer component testing - Add test_cli.py for testing command line interface functionality
1 parent cb75deb commit 2b45423

File tree

3 files changed

+874
-0
lines changed

3 files changed

+874
-0
lines changed

tests/test_buffers.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
# amaranth: UnusedElaboratable=no
2+
# SPDX-License-Identifier: BSD-2-Clause
3+
4+
import unittest
5+
from unittest import mock
6+
7+
from amaranth import Module, Signal
8+
from amaranth.lib import io
9+
10+
# We'll need to mock SiliconPlatformPort instead of using the real one
11+
@mock.patch('chipflow_lib.platforms.silicon.IOBuffer')
12+
@mock.patch('chipflow_lib.platforms.silicon.FFBuffer')
13+
class TestBuffers(unittest.TestCase):
14+
def test_io_buffer_mocked(self, mock_ffbuffer, mock_iobuffer):
15+
"""Test that IOBuffer can be imported and mocked"""
16+
from chipflow_lib.platforms.silicon import IOBuffer
17+
18+
# Verify that the mock is working
19+
self.assertEqual(IOBuffer, mock_iobuffer)
20+
21+
# Create a mock port
22+
port = mock.Mock()
23+
port.invert = False
24+
25+
# Create a mock for the IOBuffer elaborate method
26+
module = Module()
27+
mock_iobuffer.return_value.elaborate.return_value = module
28+
29+
# Create an IOBuffer instance
30+
buffer = IOBuffer(io.Direction.Input, port)
31+
32+
# Elaborate the buffer
33+
result = buffer.elaborate(None)
34+
35+
# Verify the result
36+
self.assertEqual(result, module)
37+
mock_iobuffer.return_value.elaborate.assert_called_once()
38+
39+
def test_ff_buffer_mocked(self, mock_ffbuffer, mock_iobuffer):
40+
"""Test that FFBuffer can be imported and mocked"""
41+
from chipflow_lib.platforms.silicon import FFBuffer
42+
43+
# Verify that the mock is working
44+
self.assertEqual(FFBuffer, mock_ffbuffer)
45+
46+
# Create a mock port
47+
port = mock.Mock()
48+
port.invert = False
49+
50+
# Create a mock for the FFBuffer elaborate method
51+
module = Module()
52+
mock_ffbuffer.return_value.elaborate.return_value = module
53+
54+
# Create an FFBuffer instance
55+
buffer = FFBuffer(io.Direction.Input, port, i_domain="sync", o_domain="sync")
56+
57+
# Elaborate the buffer
58+
result = buffer.elaborate(None)
59+
60+
# Verify the result
61+
self.assertEqual(result, module)
62+
mock_ffbuffer.return_value.elaborate.assert_called_once()

tests/test_cli.py

Lines changed: 212 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,212 @@
1+
# SPDX-License-Identifier: BSD-2-Clause
2+
import os
3+
import sys
4+
import unittest
5+
import argparse
6+
from unittest import mock
7+
import logging
8+
9+
from chipflow_lib import ChipFlowError
10+
from chipflow_lib.cli import run, UnexpectedError
11+
12+
13+
class MockCommand:
14+
"""Mock command for testing CLI"""
15+
def build_cli_parser(self, parser):
16+
parser.add_argument("--option", help="Test option")
17+
parser.add_argument("action", choices=["valid", "error", "unexpected"])
18+
19+
def run_cli(self, args):
20+
if args.action == "error":
21+
raise ChipFlowError("Command error")
22+
elif args.action == "unexpected":
23+
raise ValueError("Unexpected error")
24+
# Valid action does nothing
25+
26+
27+
class TestCLI(unittest.TestCase):
28+
@mock.patch("chipflow_lib.cli._parse_config")
29+
@mock.patch("chipflow_lib.cli.PinCommand")
30+
@mock.patch("chipflow_lib.cli._get_cls_by_reference")
31+
def test_run_success(self, mock_get_cls, mock_pin_command, mock_parse_config):
32+
"""Test CLI run with successful command execution"""
33+
# Setup mocks
34+
mock_config = {
35+
"chipflow": {
36+
"steps": {
37+
"test": "test:MockStep"
38+
}
39+
}
40+
}
41+
mock_parse_config.return_value = mock_config
42+
43+
mock_pin_cmd = MockCommand()
44+
mock_pin_command.return_value = mock_pin_cmd
45+
46+
mock_test_cmd = MockCommand()
47+
mock_get_cls.return_value = lambda config: mock_test_cmd
48+
49+
# Capture stdout for assertion
50+
with mock.patch("sys.stdout") as mock_stdout:
51+
# Run with valid action
52+
run(["test", "valid"])
53+
54+
# No error message should be printed
55+
mock_stdout.write.assert_not_called()
56+
57+
@mock.patch("chipflow_lib.cli._parse_config")
58+
@mock.patch("chipflow_lib.cli.PinCommand")
59+
@mock.patch("chipflow_lib.cli._get_cls_by_reference")
60+
def test_run_command_error(self, mock_get_cls, mock_pin_command, mock_parse_config):
61+
"""Test CLI run with command raising ChipFlowError"""
62+
# Setup mocks
63+
mock_config = {
64+
"chipflow": {
65+
"steps": {
66+
"test": "test:MockStep"
67+
}
68+
}
69+
}
70+
mock_parse_config.return_value = mock_config
71+
72+
mock_pin_cmd = MockCommand()
73+
mock_pin_command.return_value = mock_pin_cmd
74+
75+
mock_test_cmd = MockCommand()
76+
mock_get_cls.return_value = lambda config: mock_test_cmd
77+
78+
# Capture stdout for assertion
79+
with mock.patch("builtins.print") as mock_print:
80+
# Run with error action
81+
run(["test", "error"])
82+
83+
# Error message should be printed
84+
mock_print.assert_called_once()
85+
self.assertIn("Error while executing `test error`", mock_print.call_args[0][0])
86+
87+
@mock.patch("chipflow_lib.cli._parse_config")
88+
@mock.patch("chipflow_lib.cli.PinCommand")
89+
@mock.patch("chipflow_lib.cli._get_cls_by_reference")
90+
def test_run_unexpected_error(self, mock_get_cls, mock_pin_command, mock_parse_config):
91+
"""Test CLI run with command raising unexpected exception"""
92+
# Setup mocks
93+
mock_config = {
94+
"chipflow": {
95+
"steps": {
96+
"test": "test:MockStep"
97+
}
98+
}
99+
}
100+
mock_parse_config.return_value = mock_config
101+
102+
mock_pin_cmd = MockCommand()
103+
mock_pin_command.return_value = mock_pin_cmd
104+
105+
mock_test_cmd = MockCommand()
106+
mock_get_cls.return_value = lambda config: mock_test_cmd
107+
108+
# Capture stdout for assertion
109+
with mock.patch("builtins.print") as mock_print:
110+
# Run with unexpected error action
111+
run(["test", "unexpected"])
112+
113+
# Error message should be printed
114+
mock_print.assert_called_once()
115+
self.assertIn("Error while executing `test unexpected`", mock_print.call_args[0][0])
116+
self.assertIn("Unexpected error", mock_print.call_args[0][0])
117+
118+
@mock.patch("chipflow_lib.cli._parse_config")
119+
@mock.patch("chipflow_lib.cli.PinCommand")
120+
def test_step_init_error(self, mock_pin_command, mock_parse_config):
121+
"""Test CLI run with error initializing step"""
122+
# Setup mocks
123+
mock_config = {
124+
"chipflow": {
125+
"steps": {
126+
"test": "test:MockStep"
127+
}
128+
}
129+
}
130+
mock_parse_config.return_value = mock_config
131+
132+
mock_pin_cmd = MockCommand()
133+
mock_pin_command.return_value = mock_pin_cmd
134+
135+
# Make _get_cls_by_reference raise an exception during step initialization
136+
with mock.patch("chipflow_lib.cli._get_cls_by_reference") as mock_get_cls:
137+
mock_get_cls.return_value = mock.Mock(side_effect=Exception("Init error"))
138+
139+
with self.assertRaises(ChipFlowError) as cm:
140+
run(["test", "valid"])
141+
142+
self.assertIn("Encountered error while initializing step", str(cm.exception))
143+
144+
@mock.patch("chipflow_lib.cli._parse_config")
145+
@mock.patch("chipflow_lib.cli.PinCommand")
146+
@mock.patch("chipflow_lib.cli._get_cls_by_reference")
147+
def test_build_parser_error(self, mock_get_cls, mock_pin_command, mock_parse_config):
148+
"""Test CLI run with error building CLI parser"""
149+
# Setup mocks
150+
mock_config = {
151+
"chipflow": {
152+
"steps": {
153+
"test": "test:MockStep"
154+
}
155+
}
156+
}
157+
mock_parse_config.return_value = mock_config
158+
159+
# Make pin command raise an error during build_cli_parser
160+
mock_pin_cmd = mock.Mock()
161+
mock_pin_cmd.build_cli_parser.side_effect = Exception("Parser error")
162+
mock_pin_command.return_value = mock_pin_cmd
163+
164+
mock_test_cmd = mock.Mock()
165+
mock_test_cmd.build_cli_parser.side_effect = Exception("Parser error")
166+
mock_get_cls.return_value = lambda config: mock_test_cmd
167+
168+
with self.assertRaises(ChipFlowError) as cm:
169+
run(["pin", "lock"])
170+
171+
self.assertIn("Encountered error while building CLI argument parser", str(cm.exception))
172+
173+
@mock.patch("chipflow_lib.cli._parse_config")
174+
@mock.patch("chipflow_lib.cli.PinCommand")
175+
@mock.patch("chipflow_lib.cli._get_cls_by_reference")
176+
def test_verbosity_flags(self, mock_get_cls, mock_pin_command, mock_parse_config):
177+
"""Test CLI verbosity flags"""
178+
# Setup mocks
179+
mock_config = {
180+
"chipflow": {
181+
"steps": {
182+
"test": "test:MockStep"
183+
}
184+
}
185+
}
186+
mock_parse_config.return_value = mock_config
187+
188+
mock_pin_cmd = MockCommand()
189+
mock_pin_command.return_value = mock_pin_cmd
190+
191+
mock_test_cmd = MockCommand()
192+
mock_get_cls.return_value = lambda config: mock_test_cmd
193+
194+
# Save original log level
195+
original_level = logging.getLogger().level
196+
197+
try:
198+
# Test with -v
199+
with mock.patch("sys.stdout"):
200+
run(["-v", "test", "valid"])
201+
self.assertEqual(logging.getLogger().level, logging.INFO)
202+
203+
# Reset log level
204+
logging.getLogger().setLevel(original_level)
205+
206+
# Test with -v -v
207+
with mock.patch("sys.stdout"):
208+
run(["-v", "-v", "test", "valid"])
209+
self.assertEqual(logging.getLogger().level, logging.DEBUG)
210+
finally:
211+
# Restore original log level
212+
logging.getLogger().setLevel(original_level)

0 commit comments

Comments
 (0)