mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-10-17 19:36:28 +02:00
test: add unit tests for extract_filament_usage_from_3mf function
- Valid filament extraction (single/multiple) - Plate-specific filtering - Error handling (invalid file, missing slice_info) - Missing/invalid field handling
This commit is contained in:
@@ -4,15 +4,27 @@ Tests G-code parsing, filament length-to-weight conversion,
|
||||
and cumulative layer usage lookup.
|
||||
"""
|
||||
|
||||
import io
|
||||
import math
|
||||
import zipfile
|
||||
|
||||
from backend.app.utils.threemf_tools import (
|
||||
extract_filament_usage_from_3mf,
|
||||
get_cumulative_usage_at_layer,
|
||||
mm_to_grams,
|
||||
parse_gcode_layer_filament_usage,
|
||||
)
|
||||
|
||||
|
||||
def create_mock_3mf(slice_info_content: str) -> io.BytesIO:
|
||||
"""Create a mock 3MF file (ZIP) with slice_info.config content."""
|
||||
buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(buffer, "w") as zf:
|
||||
zf.writestr("Metadata/slice_info.config", slice_info_content)
|
||||
buffer.seek(0)
|
||||
return buffer
|
||||
|
||||
|
||||
class TestParseGcodeLayerFilamentUsage:
|
||||
"""Tests for parse_gcode_layer_filament_usage()."""
|
||||
|
||||
@@ -247,3 +259,152 @@ class TestGetCumulativeUsageAtLayer:
|
||||
"""Target layer 0."""
|
||||
data = {0: {0: 10.0}, 1: {0: 20.0}}
|
||||
assert get_cumulative_usage_at_layer(data, 0) == {0: 10.0}
|
||||
|
||||
|
||||
class TestExtractFilamentUsageFrom3mf:
|
||||
"""Tests for extract_filament_usage_from_3mf function."""
|
||||
|
||||
def test_extract_single_filament(self, tmp_path):
|
||||
"""Test extracting a single filament."""
|
||||
xml_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config>
|
||||
<filament id="1" used_g="50.5" type="PLA" color="#FF0000"/>
|
||||
</config>
|
||||
"""
|
||||
mock_3mf = create_mock_3mf(xml_content)
|
||||
file_path = tmp_path / "test.3mf"
|
||||
file_path.write_bytes(mock_3mf.read())
|
||||
|
||||
result = extract_filament_usage_from_3mf(file_path)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0]["slot_id"] == 1
|
||||
assert result[0]["used_g"] == 50.5
|
||||
assert result[0]["type"] == "PLA"
|
||||
assert result[0]["color"] == "#FF0000"
|
||||
|
||||
def test_extract_multiple_filaments(self, tmp_path):
|
||||
"""Test extracting multiple filaments."""
|
||||
xml_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config>
|
||||
<filament id="1" used_g="50.5" type="PLA" color="#FF0000"/>
|
||||
<filament id="2" used_g="30.2" type="PETG" color="#00FF00"/>
|
||||
<filament id="3" used_g="10.0" type="ABS" color="#0000FF"/>
|
||||
</config>
|
||||
"""
|
||||
mock_3mf = create_mock_3mf(xml_content)
|
||||
file_path = tmp_path / "test.3mf"
|
||||
file_path.write_bytes(mock_3mf.read())
|
||||
|
||||
result = extract_filament_usage_from_3mf(file_path)
|
||||
|
||||
assert len(result) == 3
|
||||
assert result[0]["slot_id"] == 1
|
||||
assert result[1]["slot_id"] == 2
|
||||
assert result[2]["slot_id"] == 3
|
||||
|
||||
def test_extract_filament_with_plate_id(self, tmp_path):
|
||||
"""Test extracting filament for a specific plate."""
|
||||
xml_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config>
|
||||
<plate>
|
||||
<metadata key="index" value="1"/>
|
||||
<filament id="1" used_g="25.0" type="PLA" color="#FF0000"/>
|
||||
</plate>
|
||||
<plate>
|
||||
<metadata key="index" value="2"/>
|
||||
<filament id="1" used_g="75.0" type="PETG" color="#00FF00"/>
|
||||
</plate>
|
||||
</config>
|
||||
"""
|
||||
mock_3mf = create_mock_3mf(xml_content)
|
||||
file_path = tmp_path / "test.3mf"
|
||||
file_path.write_bytes(mock_3mf.read())
|
||||
|
||||
result = extract_filament_usage_from_3mf(file_path, plate_id=2)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0]["used_g"] == 75.0
|
||||
assert result[0]["type"] == "PETG"
|
||||
|
||||
def test_missing_slice_info_returns_empty(self, tmp_path):
|
||||
"""Test that missing slice_info.config returns empty list."""
|
||||
buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(buffer, "w") as zf:
|
||||
zf.writestr("other_file.txt", "content")
|
||||
buffer.seek(0)
|
||||
|
||||
file_path = tmp_path / "test.3mf"
|
||||
file_path.write_bytes(buffer.read())
|
||||
|
||||
result = extract_filament_usage_from_3mf(file_path)
|
||||
|
||||
assert result == []
|
||||
|
||||
def test_invalid_file_returns_empty(self, tmp_path):
|
||||
"""Test that invalid file returns empty list."""
|
||||
file_path = tmp_path / "invalid.3mf"
|
||||
file_path.write_text("not a zip file")
|
||||
|
||||
result = extract_filament_usage_from_3mf(file_path)
|
||||
|
||||
assert result == []
|
||||
|
||||
def test_nonexistent_file_returns_empty(self, tmp_path):
|
||||
"""Test that nonexistent file returns empty list."""
|
||||
file_path = tmp_path / "nonexistent.3mf"
|
||||
|
||||
result = extract_filament_usage_from_3mf(file_path)
|
||||
|
||||
assert result == []
|
||||
|
||||
def test_filament_without_id_is_skipped(self, tmp_path):
|
||||
"""Test that filament without id is skipped."""
|
||||
xml_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config>
|
||||
<filament used_g="50.5" type="PLA" color="#FF0000"/>
|
||||
<filament id="2" used_g="30.0" type="PETG" color="#00FF00"/>
|
||||
</config>
|
||||
"""
|
||||
mock_3mf = create_mock_3mf(xml_content)
|
||||
file_path = tmp_path / "test.3mf"
|
||||
file_path.write_bytes(mock_3mf.read())
|
||||
|
||||
result = extract_filament_usage_from_3mf(file_path)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0]["slot_id"] == 2
|
||||
|
||||
def test_invalid_used_g_is_skipped(self, tmp_path):
|
||||
"""Test that filament with invalid used_g is skipped."""
|
||||
xml_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config>
|
||||
<filament id="1" used_g="invalid" type="PLA" color="#FF0000"/>
|
||||
<filament id="2" used_g="30.0" type="PETG" color="#00FF00"/>
|
||||
</config>
|
||||
"""
|
||||
mock_3mf = create_mock_3mf(xml_content)
|
||||
file_path = tmp_path / "test.3mf"
|
||||
file_path.write_bytes(mock_3mf.read())
|
||||
|
||||
result = extract_filament_usage_from_3mf(file_path)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0]["slot_id"] == 2
|
||||
|
||||
def test_missing_optional_fields(self, tmp_path):
|
||||
"""Test that missing type and color default to empty string."""
|
||||
xml_content = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<config>
|
||||
<filament id="1" used_g="50.5"/>
|
||||
</config>
|
||||
"""
|
||||
mock_3mf = create_mock_3mf(xml_content)
|
||||
file_path = tmp_path / "test.3mf"
|
||||
file_path.write_bytes(mock_3mf.read())
|
||||
|
||||
result = extract_filament_usage_from_3mf(file_path)
|
||||
|
||||
assert len(result) == 1
|
||||
assert result[0]["type"] == ""
|
||||
assert result[0]["color"] == ""
|
||||
|
||||
Reference in New Issue
Block a user