Skip to content

Commit 72f6589

Browse files
robtaylorclaude
andcommitted
Add comprehensive test suite for steps and software components
- Added tests for SoftwareGenerator class in software/soft_gen.py - Added tests for the step classes in steps/board.py, steps/sim.py, and steps/software.py - Added advanced tests for pin_lock module - Improved total test coverage across the codebase 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent d654ae6 commit 72f6589

File tree

3 files changed

+431
-0
lines changed

3 files changed

+431
-0
lines changed

tests/test_pin_lock_advanced.py

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# SPDX-License-Identifier: BSD-2-Clause
2+
import unittest
3+
from unittest import mock
4+
import tempfile
5+
import os
6+
import json
7+
from pathlib import Path
8+
9+
from chipflow_lib.platforms.utils import LockFile, Package, Port
10+
from chipflow_lib import ChipFlowError
11+
from chipflow_lib.pin_lock import (
12+
lock_pins,
13+
count_member_pins,
14+
allocate_pins,
15+
PinCommand
16+
)
17+
18+
19+
class TestPinLockAdvanced(unittest.TestCase):
20+
def setUp(self):
21+
# Create a temporary directory for tests
22+
self.temp_dir = tempfile.TemporaryDirectory()
23+
self.original_cwd = os.getcwd()
24+
os.chdir(self.temp_dir.name)
25+
26+
# Mock environment variables
27+
self.env_patcher = mock.patch.dict(os.environ, {"CHIPFLOW_ROOT": self.temp_dir.name})
28+
self.env_patcher.start()
29+
30+
# Create test data
31+
self.mock_config = {
32+
"chipflow": {
33+
"silicon": {
34+
"package": "pga144",
35+
"pads": {
36+
"pad1": {"type": "io", "loc": "1"},
37+
"pad2": {"type": "clock", "loc": "2"}
38+
},
39+
"power": {
40+
"vdd": {"type": "power", "loc": "3"},
41+
"vss": {"type": "ground", "loc": "4"}
42+
}
43+
},
44+
"clocks": {
45+
"default": "sys_clk"
46+
},
47+
"resets": {
48+
"default": "sys_rst_n"
49+
},
50+
"top": {
51+
"component1": "module:Component"
52+
}
53+
}
54+
}
55+
56+
def tearDown(self):
57+
self.env_patcher.stop()
58+
os.chdir(self.original_cwd)
59+
self.temp_dir.cleanup()
60+
61+
62+
63+
class TestPinCommandCLI(unittest.TestCase):
64+
def test_build_cli_parser(self):
65+
"""Test build_cli_parser method"""
66+
# Create mock parser
67+
parser = mock.Mock()
68+
subparsers = mock.Mock()
69+
parser.add_subparsers.return_value = subparsers
70+
71+
# Create PinCommand
72+
cmd = PinCommand({"test": "config"})
73+
74+
# Call build_cli_parser
75+
cmd.build_cli_parser(parser)
76+
77+
# Check that add_subparsers was called
78+
parser.add_subparsers.assert_called_once()
79+
# Check that add_parser was called with "lock"
80+
subparsers.add_parser.assert_called_once_with("lock", help=mock.ANY)
81+
82+
def test_run_cli_lock(self):
83+
"""Test run_cli method with lock action"""
84+
# Create mock args
85+
args = mock.Mock()
86+
args.action = "lock"
87+
88+
# Create PinCommand
89+
cmd = PinCommand({"test": "config"})
90+
91+
# Patch lock method
92+
with mock.patch.object(cmd, "lock") as mock_lock:
93+
# Call run_cli
94+
cmd.run_cli(args)
95+
96+
# Check that lock was called
97+
mock_lock.assert_called_once()

tests/test_software_generator.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# SPDX-License-Identifier: BSD-2-Clause
2+
import os
3+
import tempfile
4+
import unittest
5+
from pathlib import Path
6+
from unittest import mock
7+
8+
from chipflow_lib.software.soft_gen import SoftwareGenerator
9+
10+
11+
class TestSoftwareGenerator(unittest.TestCase):
12+
def setUp(self):
13+
"""Set up the test with a SoftwareGenerator instance"""
14+
self.rom_start = 0x10000000
15+
self.rom_size = 0x8000
16+
self.ram_start = 0x20000000
17+
self.ram_size = 0x4000
18+
self.generator = SoftwareGenerator(
19+
rom_start=self.rom_start,
20+
rom_size=self.rom_size,
21+
ram_start=self.ram_start,
22+
ram_size=self.ram_size
23+
)
24+
25+
def test_initialization(self):
26+
"""Test that the SoftwareGenerator initializes correctly"""
27+
self.assertEqual(self.generator.rom_start, self.rom_start)
28+
self.assertEqual(self.generator.rom_size, self.rom_size)
29+
self.assertEqual(self.generator.ram_start, self.ram_start)
30+
self.assertEqual(self.generator.ram_size, self.ram_size)
31+
self.assertEqual(self.generator.defines, [])
32+
self.assertEqual(self.generator.periphs, [])
33+
self.assertEqual(self.generator.extra_init, [])
34+
35+
def test_add_periph(self):
36+
"""Test adding peripherals"""
37+
self.generator.add_periph("uart", "uart0", 0x40000000)
38+
self.generator.add_periph("gpio", "gpio0", 0x40001000)
39+
40+
self.assertEqual(len(self.generator.periphs), 2)
41+
self.assertEqual(self.generator.periphs[0], ("uart", "uart0", 0x40000000))
42+
self.assertEqual(self.generator.periphs[1], ("gpio", "gpio0", 0x40001000))
43+
44+
def test_add_extra_init(self):
45+
"""Test adding extra initialization code"""
46+
init_code = "# This is a test init code"
47+
self.generator.add_extra_init(init_code)
48+
49+
self.assertEqual(len(self.generator.extra_init), 1)
50+
self.assertEqual(self.generator.extra_init[0], init_code)
51+
52+
def test_soc_h_with_uart(self):
53+
"""Test soc.h generation with a UART peripheral"""
54+
self.generator.add_periph("uart", "uart0", 0x40000000)
55+
56+
soc_h = self.generator.soc_h
57+
58+
# Check that the UART header is included
59+
self.assertIn('#include "drivers/uart.h"', soc_h)
60+
61+
# Check that the UART is defined
62+
self.assertIn('#define uart0 ((volatile uart_regs_t *const)0x40000000)', soc_h)
63+
64+
# Check that putc, puts, and puthex are defined to use uart0
65+
self.assertIn('#define putc(x) uart_putc(uart0, x)', soc_h)
66+
self.assertIn('#define puts(x) uart_puts(uart0, x)', soc_h)
67+
self.assertIn('#define puthex(x) uart_puthex(uart0, x)', soc_h)
68+
69+
def test_soc_h_without_uart(self):
70+
"""Test soc.h generation without a UART peripheral"""
71+
self.generator.add_periph("gpio", "gpio0", 0x40001000)
72+
73+
soc_h = self.generator.soc_h
74+
75+
# Check that the GPIO header is included
76+
self.assertIn('#include "drivers/gpio.h"', soc_h)
77+
78+
# Check that the GPIO is defined
79+
self.assertIn('#define gpio0 ((volatile gpio_regs_t *const)0x40001000)', soc_h)
80+
81+
# Check that putc, puts, and puthex are defined as no-ops
82+
self.assertIn('#define putc(x) do {{ (void)x; }} while(0)', soc_h)
83+
self.assertIn('#define puts(x) do {{ (void)x; }} while(0)', soc_h)
84+
self.assertIn('#define puthex(x) do {{ (void)x; }} while(0)', soc_h)
85+
86+
def test_start_assembly(self):
87+
"""Test start.S generation"""
88+
init_code = "# Custom initialization"
89+
self.generator.add_extra_init(init_code)
90+
91+
start_code = self.generator.start
92+
93+
# Check that the stack pointer is set to the top of RAM
94+
self.assertIn(f"li x2, 0x{self.ram_start + self.ram_size:08x}", start_code)
95+
96+
# Check that our custom initialization code is included
97+
self.assertIn(init_code, start_code)
98+
99+
# Check essential parts of the startup code
100+
self.assertIn("call main", start_code)
101+
self.assertIn("loop:", start_code)
102+
103+
def test_linker_script(self):
104+
"""Test sections.lds generation"""
105+
lds = self.generator.lds
106+
107+
# Check memory regions
108+
self.assertIn(f"FLASH (rx) : ORIGIN = 0x{self.rom_start:08x}, LENGTH = 0x{self.rom_size:08x}", lds)
109+
self.assertIn(f"RAM (xrw) : ORIGIN = 0x{self.ram_start:08x}, LENGTH = 0x{self.ram_size:08x}", lds)
110+
111+
# Check essential sections
112+
self.assertIn(".text :", lds)
113+
self.assertIn(".data :", lds)
114+
self.assertIn(".bss :", lds)
115+
self.assertIn(".heap :", lds)
116+
117+
def test_generate(self):
118+
"""Test file generation"""
119+
with tempfile.TemporaryDirectory() as temp_dir:
120+
# Generate the files
121+
self.generator.generate(temp_dir)
122+
123+
# Check that the files were created
124+
self.assertTrue(os.path.exists(os.path.join(temp_dir, "start.S")))
125+
self.assertTrue(os.path.exists(os.path.join(temp_dir, "sections.lds")))
126+
self.assertTrue(os.path.exists(os.path.join(temp_dir, "soc.h")))
127+
128+
# Verify the content of the files
129+
with open(os.path.join(temp_dir, "start.S"), "r") as f:
130+
self.assertEqual(f.read(), self.generator.start)
131+
132+
with open(os.path.join(temp_dir, "sections.lds"), "r") as f:
133+
self.assertEqual(f.read(), self.generator.lds)
134+
135+
with open(os.path.join(temp_dir, "soc.h"), "r") as f:
136+
self.assertEqual(f.read(), self.generator.soc_h)

0 commit comments

Comments
 (0)