""" Unit tests for ezdxf helper functions in utils.py Tests cover: - Attribute extraction from INSERT blocks - Layer creation and management - Geometry creation (rectangles, text) - Block reference operations - Entity querying """ import pytest import ezdxf from pathlib import Path import sys # Add lib directory to path for imports sys.path.insert(0, str(Path(__file__).parent.parent / 'lib')) from utils import ( extract_insert_attributes, extract_insert_attributes_with_doc, extract_attributes_with_positions, ensure_layer_exists, add_rectangle_lwpolyline, add_text_with_alignment, add_blockref_with_attributes, query_entities_by_layer, create_layers_from_config ) @pytest.fixture def doc(): """Create a new DXF document for testing""" return ezdxf.new('R2010') @pytest.fixture def msp(doc): """Get modelspace from document""" return doc.modelspace() class TestAttributeExtraction: """Tests for attribute extraction functions""" def test_extract_insert_attributes_with_doc_simple(self, doc, msp): """Test extracting attributes from a simple block with direct attributes""" # Create block with attributes block = doc.blocks.new('TEST_BLOCK') block.add_attdef('IO', (0, 0), dxfattribs={'height': 2.5}) block.add_attdef('KENNZEICHNUNG', (0, 5), dxfattribs={'height': 2.5}) # Insert block and set attributes insert = msp.add_blockref('TEST_BLOCK', (10, 10)) insert.add_auto_attribs({'IO': 'MA0001', 'KENNZEICHNUNG': '=A01+UH00'}) # Extract attributes attrs = extract_insert_attributes_with_doc(doc, insert) assert attrs['IO'] == 'MA0001' assert attrs['KENNZEICHNUNG'] == '=A01+UH00' def test_extract_insert_attributes_empty_block(self, doc, msp): """Test extracting attributes from block without attributes""" block = doc.blocks.new('EMPTY_BLOCK') insert = msp.add_blockref('EMPTY_BLOCK', (10, 10)) attrs = extract_insert_attributes_with_doc(doc, insert) assert attrs == {} def test_extract_insert_attributes_non_insert(self, doc, msp): """Test that non-INSERT entities return empty dict""" line = msp.add_line((0, 0), (10, 10)) attrs = extract_insert_attributes_with_doc(doc, line) assert attrs == {} def test_extract_attributes_with_positions(self, doc, msp): """Test extracting attributes with position information""" # Create block with attributes block = doc.blocks.new('POS_TEST') block.add_attdef('IO', (0, 0), dxfattribs={'height': 2.5}) block.add_attdef('NAME', (0, 5), dxfattribs={'height': 2.5}) # Insert blocks insert1 = msp.add_blockref('POS_TEST', (100, 200)) insert1.add_auto_attribs({'IO': 'MA0001', 'NAME': 'Motor1'}) insert2 = msp.add_blockref('POS_TEST', (300, 400)) insert2.add_auto_attribs({'IO': 'MA0002', 'NAME': 'Motor2'}) # Extract with positions all_inserts, all_positions = extract_attributes_with_positions(msp) assert len(all_inserts) == 2 assert len(all_positions) == 2 assert all_inserts[0]['IO'] == 'MA0001' assert all_inserts[1]['IO'] == 'MA0002' class TestLayerOperations: """Tests for layer creation and management functions""" def test_ensure_layer_exists_new_layer(self, doc): """Test creating a new layer""" layer = ensure_layer_exists(doc, 'TEST_LAYER', color=5) assert 'TEST_LAYER' in doc.layers assert doc.layers.get('TEST_LAYER').color == 5 def test_ensure_layer_exists_existing_layer(self, doc): """Test that existing layer is returned unchanged""" # Create layer first doc.layers.add('EXISTING', color=1) # Ensure it exists (should not error) layer = ensure_layer_exists(doc, 'EXISTING', color=3) assert 'EXISTING' in doc.layers # Color should be updated assert doc.layers.get('EXISTING').color == 3 def test_ensure_layer_with_all_options(self, doc): """Test creating layer with all options""" layer = ensure_layer_exists( doc, 'FULL_LAYER', color=5, linetype='DASHED', lineweight=25, description='Test layer description' ) assert 'FULL_LAYER' in doc.layers layer_obj = doc.layers.get('FULL_LAYER') assert layer_obj.color == 5 assert layer_obj.dxf.linetype == 'DASHED' assert layer_obj.dxf.lineweight == 25 def test_create_layers_from_config(self, doc): """Test creating multiple layers from configuration""" layer_config = { 'ILS_MOTOR': {'color': 1, 'description': 'Motor layer'}, 'ILS_SENSOR': {'color': 3, 'lineweight': 25}, 'ILS_CABLE': {'color': 5} } create_layers_from_config(doc, layer_config) assert 'ILS_MOTOR' in doc.layers assert 'ILS_SENSOR' in doc.layers assert 'ILS_CABLE' in doc.layers assert doc.layers.get('ILS_MOTOR').color == 1 assert doc.layers.get('ILS_SENSOR').color == 3 assert doc.layers.get('ILS_CABLE').color == 5 class TestGeometryCreation: """Tests for geometry creation functions""" def test_add_rectangle_lwpolyline_simple(self, doc, msp): """Test adding a simple rectangle""" rect = add_rectangle_lwpolyline(msp, 0, 0, 100, 50) assert rect.dxftype() == 'LWPOLYLINE' assert rect.closed is True assert len(list(rect.get_points())) == 4 def test_add_rectangle_with_color(self, doc, msp): """Test adding rectangle with color""" rect = add_rectangle_lwpolyline(msp, 10, 10, 200, 100, color=5) assert rect.dxf.color == 5 def test_add_rectangle_with_rgb(self, doc, msp): """Test adding rectangle with RGB color""" rect = add_rectangle_lwpolyline(msp, 20, 20, 150, 75, rgb=(255, 0, 0)) assert rect.rgb == (255, 0, 0) def test_add_rectangle_on_layer(self, doc, msp): """Test adding rectangle on specific layer""" ensure_layer_exists(doc, 'TEST_LAYER') rect = add_rectangle_lwpolyline(msp, 0, 0, 100, 50, layer='TEST_LAYER') assert rect.dxf.layer == 'TEST_LAYER' def test_add_text_with_alignment_simple(self, doc, msp): """Test adding simple text""" text = add_text_with_alignment(msp, "Test", 100, 100) assert text.dxftype() == 'TEXT' assert text.dxf.text == "Test" def test_add_text_with_rotation(self, doc, msp): """Test adding text with rotation""" text = add_text_with_alignment(msp, "Rotated", 200, 200, rotation=45.0) assert text.dxf.rotation == 45.0 def test_add_text_with_color_and_height(self, doc, msp): """Test adding text with color and height""" text = add_text_with_alignment(msp, "Styled", 300, 300, height=75, color=3) assert text.dxf.height == 75 assert text.dxf.color == 3 class TestBlockOperations: """Tests for block reference operations""" def test_add_blockref_without_attributes(self, doc, msp): """Test adding block reference without attributes""" # Create a simple block block = doc.blocks.new('SIMPLE_BLOCK') block.add_line((0, 0), (10, 10)) # Add block reference insert = add_blockref_with_attributes(msp, 'SIMPLE_BLOCK', (100, 100)) assert insert.dxftype() == 'INSERT' assert insert.dxf.name == 'SIMPLE_BLOCK' def test_add_blockref_with_attributes(self, doc, msp): """Test adding block reference with attributes""" # Create block with attribute definitions block = doc.blocks.new('ATTR_BLOCK') block.add_attdef('IO', (0, 0)) block.add_attdef('NAME', (0, 5)) # Add block reference with attributes attrs = {'IO': 'MA0001', 'NAME': 'Motor'} insert = add_blockref_with_attributes( msp, 'ATTR_BLOCK', (200, 200), attributes=attrs ) assert insert.dxf.name == 'ATTR_BLOCK' # Check attributes were set attrib_values = {a.dxf.tag: a.dxf.text for a in insert.attribs} assert attrib_values['IO'] == 'MA0001' assert attrib_values['NAME'] == 'Motor' def test_add_blockref_with_scale_and_rotation(self, doc, msp): """Test adding block reference with scale and rotation""" block = doc.blocks.new('TRANSFORM_BLOCK') block.add_line((0, 0), (10, 10)) insert = add_blockref_with_attributes( msp, 'TRANSFORM_BLOCK', (300, 300), scale=0.5, rotation=90.0 ) assert insert.dxf.xscale == 0.5 assert insert.dxf.yscale == 0.5 assert insert.dxf.rotation == 90.0 class TestEntityQuerying: """Tests for entity querying functions""" def test_query_entities_by_layer_single_layer(self, doc, msp): """Test querying entities on a single layer""" ensure_layer_exists(doc, 'MOTOR') ensure_layer_exists(doc, 'SENSOR') # Add entities on different layers msp.add_line((0, 0), (10, 10), dxfattribs={'layer': 'MOTOR'}) msp.add_line((0, 0), (20, 20), dxfattribs={'layer': 'SENSOR'}) msp.add_line((0, 0), (30, 30), dxfattribs={'layer': 'MOTOR'}) # Query only MOTOR layer motor_entities = query_entities_by_layer(msp, 'LINE', layers=['MOTOR']) assert len(motor_entities) == 2 assert all(e.dxf.layer == 'MOTOR' for e in motor_entities) def test_query_entities_by_type(self, doc, msp): """Test querying entities by type""" msp.add_line((0, 0), (10, 10)) msp.add_circle((0, 0), 5) msp.add_line((0, 0), (20, 20)) lines = query_entities_by_layer(msp, 'LINE') assert len(lines) == 2 assert all(e.dxftype() == 'LINE' for e in lines) def test_query_entities_exclude_layers(self, doc, msp): """Test querying entities excluding certain layers""" msp.add_line((0, 0), (10, 10), dxfattribs={'layer': '0'}) msp.add_line((0, 0), (20, 20), dxfattribs={'layer': 'ILS'}) msp.add_line((0, 0), (30, 30), dxfattribs={'layer': '0'}) # Exclude layer '0' entities = query_entities_by_layer(msp, 'LINE', exclude_layers=['0']) assert len(entities) == 1 assert entities[0].dxf.layer == 'ILS' def test_query_entities_multiple_layers(self, doc, msp): """Test querying entities from multiple layers""" msp.add_line((0, 0), (10, 10), dxfattribs={'layer': 'L1'}) msp.add_line((0, 0), (20, 20), dxfattribs={'layer': 'L2'}) msp.add_line((0, 0), (30, 30), dxfattribs={'layer': 'L3'}) entities = query_entities_by_layer(msp, 'LINE', layers=['L1', 'L2']) assert len(entities) == 2 assert set(e.dxf.layer for e in entities) == {'L1', 'L2'} if __name__ == '__main__': pytest.main([__file__, '-v'])