"""Unit tests for the geometry helpers in mckicad.patterns._geometry. These tests do NOT require kicad-sch-api — they test pure math and string matching functions. """ import pytest class TestSnapToGrid: """Tests for snap_to_grid().""" def test_exact_grid_point(self): from mckicad.patterns._geometry import snap_to_grid assert snap_to_grid(2.54) == 2.54 assert snap_to_grid(5.08) == 5.08 assert snap_to_grid(0.0) == 0.0 def test_snap_up(self): from mckicad.patterns._geometry import snap_to_grid # 2.0 is closer to 2.54 than 0.0 with grid=2.54 assert snap_to_grid(2.0) == 2.54 def test_snap_down(self): from mckicad.patterns._geometry import snap_to_grid # 1.0 is closer to 0.0 than 2.54 assert snap_to_grid(1.0) == 0.0 def test_negative_values(self): from mckicad.patterns._geometry import snap_to_grid assert snap_to_grid(-2.54) == -2.54 assert snap_to_grid(-1.0) == 0.0 def test_custom_grid(self): from mckicad.patterns._geometry import snap_to_grid # Fine grid (1.27mm) assert snap_to_grid(1.0, grid=1.27) == 1.27 assert snap_to_grid(0.5, grid=1.27) == 0.0 def test_large_values(self): from mckicad.patterns._geometry import snap_to_grid result = snap_to_grid(100.0) assert result % 2.54 == pytest.approx(0.0, abs=0.001) class TestGridPositions: """Tests for grid_positions().""" def test_single_position(self): from mckicad.patterns._geometry import grid_positions positions = grid_positions(100, 100, 1) assert len(positions) == 1 # snap_to_grid(100) = round(100/2.54)*2.54 = 39*2.54 = 99.06 assert positions[0] == (pytest.approx(99.06, abs=0.01), pytest.approx(99.06, abs=0.01)) def test_row_layout(self): from mckicad.patterns._geometry import grid_positions positions = grid_positions(0, 0, 3, cols=6, h_spacing=10.0) assert len(positions) == 3 # All should be on same row (same y) ys = [p[1] for p in positions] assert all(y == ys[0] for y in ys) def test_wraps_to_next_row(self): from mckicad.patterns._geometry import grid_positions positions = grid_positions(0, 0, 4, cols=2, h_spacing=10.0, v_spacing=15.0) assert len(positions) == 4 # First two on row 0, next two on row 1 assert positions[0][1] == positions[1][1] # same row assert positions[2][1] == positions[3][1] # same row assert positions[2][1] != positions[0][1] # different rows def test_zero_count(self): from mckicad.patterns._geometry import grid_positions positions = grid_positions(100, 100, 0) assert positions == [] def test_all_positions_grid_aligned(self): from mckicad.patterns._geometry import grid_positions, snap_to_grid positions = grid_positions(100, 100, 12, cols=4) for x, y in positions: # Verify each coordinate is unchanged by snap_to_grid # (i.e., already on the grid) assert snap_to_grid(x) == pytest.approx(x, abs=0.001) assert snap_to_grid(y) == pytest.approx(y, abs=0.001) class TestIsGroundNet: """Tests for is_ground_net().""" def test_standard_grounds(self): from mckicad.patterns._geometry import is_ground_net assert is_ground_net("GND") is True assert is_ground_net("GNDA") is True assert is_ground_net("GNDD") is True assert is_ground_net("VSS") is True assert is_ground_net("VSSA") is True def test_extended_grounds(self): from mckicad.patterns._geometry import is_ground_net assert is_ground_net("AGND") is True assert is_ground_net("DGND") is True assert is_ground_net("PGND") is True assert is_ground_net("SGND") is True def test_case_insensitive(self): from mckicad.patterns._geometry import is_ground_net assert is_ground_net("gnd") is True assert is_ground_net("Gnd") is True assert is_ground_net("vss") is True def test_not_ground(self): from mckicad.patterns._geometry import is_ground_net assert is_ground_net("VCC") is False assert is_ground_net("+3V3") is False assert is_ground_net("+5V") is False assert is_ground_net("SPI_CLK") is False assert is_ground_net("") is False def test_whitespace_stripped(self): from mckicad.patterns._geometry import is_ground_net assert is_ground_net(" GND ") is True class TestResolvePowerLibId: """Tests for resolve_power_lib_id().""" def test_known_symbols(self): from mckicad.patterns._geometry import resolve_power_lib_id assert resolve_power_lib_id("GND") == "power:GND" assert resolve_power_lib_id("VCC") == "power:VCC" assert resolve_power_lib_id("+5V") == "power:+5V" assert resolve_power_lib_id("+3V3") == "power:+3V3" def test_case_insensitive_lookup(self): from mckicad.patterns._geometry import resolve_power_lib_id assert resolve_power_lib_id("gnd") == "power:GND" assert resolve_power_lib_id("vcc") == "power:VCC" def test_unknown_falls_back_to_power_prefix(self): from mckicad.patterns._geometry import resolve_power_lib_id assert resolve_power_lib_id("+1V8") == "power:+1V8" assert resolve_power_lib_id("VDDIO") == "power:VDDIO"