woodshop/tests/test_cutlist.py

102 lines
3.4 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Tests for the cut list / board-feet / shopping estimate."""
import pytest
from woodshop.cutlist import board_feet, cut_rows, nominal_dims, shopping
from woodshop.scene import Scene
def test_nominal_dims():
assert nominal_dims("2x4") == (2.0, 4.0)
assert nominal_dims("4x4") == (4.0, 4.0)
def test_board_feet_uses_nominal():
# 2x4 at 96in = (2*4*96)/144 = 5.333 bd-ft
assert board_feet("2x4", 96) == pytest.approx(5.3333, abs=1e-3)
def test_cut_rows_groups_and_counts():
s = Scene()
s.place("2x4", 48)
s.place("2x4", 48)
s.place("2x4", 29)
rows = cut_rows(s)
by_len = {r["length_in"]: r for r in rows}
assert by_len[48.0]["count"] == 2
assert by_len[29.0]["count"] == 1
assert by_len[48.0]["board_feet"] == pytest.approx(board_feet("2x4", 48) * 2)
def test_shopping_rounds_up_with_waste():
s = Scene()
s.place("2x4", 48)
s.place("2x4", 48) # 96in total -> with 10% waste = 105.6 -> 2 sticks of 96in
assert shopping(s) == {"2x4": 2}
def test_empty_shopping():
assert shopping(Scene()) == {}
def test_end_tenon_extends_cut_length():
from woodshop.cutlist import cut_length
s = Scene()
s.place("2x4", 24)
assert cut_length(s.get_part("p1")) == 24
s.add_feature("p1", "tenon", face="end_b", depth_in=1.5) # protrudes 1.5"
assert cut_length(s.get_part("p1")) == 25.5
# the cut row and board-feet reflect the longer piece
assert cut_rows(s)[0]["length_in"] == 25.5
assert cut_rows(s)[0]["board_feet"] == pytest.approx(board_feet("2x4", 25.5))
def test_plywood_uses_sqft_and_sheets():
from woodshop.cutlist import cut_rows, shopping, format_cutlist
s = Scene()
s.place("ply-3/4", 48, width_in=24) # 48 × 24 = 8 sq ft
row = cut_rows(s)[0]
assert row["plywood"] and row["sq_ft"] == pytest.approx(8.0)
assert shopping(s)["ply-3/4"] == 1 # well under a 32 sq-ft sheet
assert "sq ft" in format_cutlist(s) and "sheet" in format_cutlist(s)
def test_cut_feature_does_not_change_cut_length():
from woodshop.cutlist import cut_length
s = Scene()
s.place("2x4", 24)
s.add_feature("p1", "mortise", face="top", width_in=1, height_in=1, depth_in=0.5)
assert cut_length(s.get_part("p1")) == 24 # cuts don't reduce stock you buy
def test_cli_cutlist_matches_bom_shopping_counts():
"""The CLI cut list now renders from the same CutPlan the BOM window uses,
so kerf-aware shopping counts agree."""
from collections import Counter
from woodshop.cutlist import format_cutlist
from woodshop.cutplan import build_cut_plan
s = Scene()
for _ in range(5):
s.place("2x4", 50) # 50" pieces: kerf prevents 2 per 96" stick
plan = build_cut_plan(s)
counts = Counter(sp.stock for sp in plan.stock_pieces)
assert f"{counts['2x4']} × 2x4 stick(s)" in format_cutlist(s)
def test_cli_cutlist_shows_species_and_sanding():
from woodshop.cutlist import format_cutlist
s = Scene()
s.place("1x4", 24)
s.set_material("p1", "oak")
s.set_finish("p1", "sanded")
text = format_cutlist(s)
assert "oak 1x4" in text # species surfaced
assert "sand to final" in text # sanding allowance surfaced
def test_cli_cutlist_flags_unplaced():
from woodshop.cutlist import format_cutlist
s = Scene()
s.place("2x4", 200) # longer than a stick
assert "WON'T FIT" in format_cutlist(s)