1# Copyright (c) 2019 Manfred Moitzi 2# License: MIT License 3import pytest 4from copy import deepcopy 5from ezdxf.math import Vec3 6from ezdxf.entities.dxfentity import ( 7 base_class, DXFAttributes, DXFNamespace, SubclassProcessor 8) 9from ezdxf.lldxf.attributes import ( 10 group_code_mapping, DefSubclass, DXFAttr, XType 11) 12from ezdxf.entities.dxfgfx import acdb_entity 13from ezdxf.entities.line import acdb_line 14from ezdxf.lldxf.extendedtags import ExtendedTags 15from ezdxf.lldxf.const import DXFAttributeError 16from ezdxf.lldxf.tagwriter import TagCollector 17from ezdxf.lldxf.tags import Tags, DXFTag 18 19 20class DXFEntity: 21 """ Mockup """ 22 DXFTYPE = 'DXFENTITY' 23 DXFATTRIBS = DXFAttributes(base_class, acdb_entity, acdb_line) 24 25 26@pytest.fixture 27def entity(): 28 return DXFEntity() 29 30 31@pytest.fixture 32def processor(): 33 return SubclassProcessor(ExtendedTags.from_text(TEST_1)) 34 35 36def test_handle_and_owner(entity, processor): 37 attribs = DXFNamespace(processor, entity) 38 assert attribs.handle == 'FFFF' 39 assert attribs.owner == 'ABBA' 40 assert attribs._entity is entity 41 42 43def test_default_values(entity, processor): 44 attribs = DXFNamespace(processor, entity) 45 assert attribs.layer == '0' 46 assert attribs.color == 256 47 assert attribs.linetype == 'BYLAYER' 48 # this attributes do not really exist 49 assert attribs.hasattr('layer') is False 50 assert attribs.hasattr('color') is False 51 assert attribs.hasattr('linetype') is False 52 53 54def test_get_value_with_default(entity, processor): 55 attribs = DXFNamespace(processor, entity) 56 # return existing values 57 assert attribs.get('handle', '0') == 'FFFF' 58 # return given default value not DXF default value, which would be '0' 59 assert attribs.get('layer', 'mozman') == 'mozman' 60 # attribute has to a valid DXF attribute 61 with pytest.raises(DXFAttributeError): 62 _ = attribs.get('hallo', 0) 63 64 # attribs without default returns None -> will not exported to DXF file 65 assert attribs.color_name is None 66 67 68def test_set_values(entity, processor): 69 attribs = DXFNamespace(processor, entity) 70 attribs.handle = 'CDEF' 71 assert attribs.handle == 'CDEF' 72 attribs.set('owner', 'DADA') 73 assert attribs.owner == 'DADA' 74 # set new attribute 75 attribs.color = 7 76 assert attribs.color == 7 77 attribs.set('linetype', 'DOT') 78 assert attribs.linetype == 'DOT' 79 # attribute has to a valid DXF attribute 80 with pytest.raises(DXFAttributeError): 81 attribs.hallo = 0 82 with pytest.raises(DXFAttributeError): 83 attribs.set('hallo', 0) 84 85 86def test_value_types(entity, processor): 87 attribs = DXFNamespace(processor, entity) 88 attribs.handle = None # None is always accepted, attribute is ignored at export 89 assert attribs.handle is None 90 attribs.handle = 'XYZ' 91 assert attribs.handle == 'XYZ', 'handle is just a string' 92 attribs.handle = 123 93 assert attribs.handle == '123', 'handle is just a string' 94 with pytest.raises(ValueError): 95 attribs.color = 'xxx' 96 97 attribs.start = (1, 2, 3) # type: Vec3 98 assert attribs.start == (1, 2, 3) 99 assert attribs.start.x == 1 100 assert attribs.start.y == 2 101 assert attribs.start.z == 3 102 103 104def test_delete_attribs(entity, processor): 105 attribs = DXFNamespace(processor, entity) 106 attribs.layer = 'mozman' 107 assert attribs.layer == 'mozman' 108 del attribs.layer 109 110 # default value 111 assert attribs.layer == '0' 112 with pytest.raises(DXFAttributeError): 113 del attribs.color 114 attribs.discard('color') # delete silently if not exist 115 with pytest.raises(DXFAttributeError): 116 del attribs.unsupported_attribute 117 118 119def test_is_supported(entity, processor): 120 attribs = DXFNamespace(processor, entity) 121 assert attribs.is_supported('linetype') is True 122 assert attribs.is_supported('true_color') is True # ezdxf does not care about DXF versions at runtime 123 assert attribs.is_supported('xxx_mozman_xxx') is False 124 125 126def test_dxftype(entity, processor): 127 attribs = DXFNamespace(processor, entity) 128 assert attribs.dxftype == 'DXFENTITY' 129 130 131def test_cloning(entity, processor): 132 attribs = DXFNamespace(processor, entity) 133 attribs.color = 77 134 attribs2 = attribs.copy(entity) 135 # clone everything 136 assert attribs2._entity is attribs._entity 137 assert attribs2.handle is attribs.handle 138 assert attribs2.owner is attribs.owner 139 assert attribs2.color == 77 140 # do not harm original entity 141 assert attribs._entity is entity 142 assert attribs.handle == 'FFFF' 143 assert attribs.owner == 'ABBA' 144 # change clone 145 attribs2.color = 13 146 assert attribs.color == 77 147 assert attribs2.color == 13 148 149 150def test_deepcopy_usage(entity, processor): 151 attribs = DXFNamespace(processor, entity) 152 attribs.color = 77 153 154 attribs2 = deepcopy(attribs) 155 # clone everything 156 assert attribs2._entity is attribs._entity 157 assert attribs2.handle is attribs.handle 158 assert attribs2.owner is attribs.owner 159 assert attribs2.color == 77 160 # do not harm original entity 161 assert attribs._entity is entity 162 assert attribs.handle == 'FFFF' 163 assert attribs.owner == 'ABBA' 164 # change clone 165 attribs2.color = 13 166 assert attribs.color == 77 167 assert attribs2.color == 13 168 169 170def test_dxf_export_one_attribute(entity, processor): 171 attribs = DXFNamespace(processor, entity) 172 tagwriter = TagCollector() 173 attribs.export_dxf_attribs(tagwriter, 'handle') 174 assert len(tagwriter.tags) == 1 175 assert tagwriter.tags[0] == (5, 'FFFF') 176 with pytest.raises(DXFAttributeError): 177 attribs.export_dxf_attribute(tagwriter, 'mozman') 178 179 180def test_dxf_export_two_attribute(entity, processor): 181 attribs = DXFNamespace(processor, entity) 182 tagwriter = TagCollector() 183 attribs.export_dxf_attribs(tagwriter, ['handle', 'owner']) 184 assert len(tagwriter.tags) == 2 185 assert tagwriter.tags[0] == (5, 'FFFF') 186 assert tagwriter.tags[1] == (330, 'ABBA') 187 188 189@pytest.fixture 190def subclass(): 191 return DefSubclass('AcDbTest', { 192 'test1': DXFAttr(1), 193 'test2': DXFAttr(2), 194 'test3': DXFAttr(1), # duplicate group code 195 'callback': DXFAttr(3, xtype=XType.callback), 196 'none_callback': DXFAttr(3), # duplicate group code 197 }) 198 199 200@pytest.fixture 201def cls(subclass): 202 class TestEntity(DXFEntity): 203 DXFATTRIBS = DXFAttributes(subclass) 204 return TestEntity 205 206 207def load_tags_fast(cls, subclass, data): 208 ns = DXFNamespace(entity=cls()) 209 mapping = group_code_mapping(subclass) 210 proc = SubclassProcessor(ExtendedTags(tags=data)) 211 unprocessed_tags = proc.fast_load_dxfattribs(ns, mapping, 0) 212 return ns, unprocessed_tags 213 214 215def test_if_fast_load_handles_duplicate_group_codes(cls, subclass): 216 data = Tags([ 217 DXFTag(0, 'ENTITY'), # First subclass tag should be ignored 218 DXFTag(1, '1'), # duplicate group code 219 DXFTag(2, '2'), 220 DXFTag(1, '3'), # duplicate group code 221 DXFTag(7, 'unprocessed tag'), 222 ]) 223 ns, unprocessed_tags = load_tags_fast(cls, subclass, data) 224 assert ns.test1 == '1' 225 assert ns.test2 == '2' 226 assert ns.test3 == '3' 227 assert len(unprocessed_tags) == 1 228 assert unprocessed_tags[0] == (7, 'unprocessed tag') 229 230 231def test_if_fast_load_handles_callback_group_codes(cls, subclass): 232 data = Tags([ 233 DXFTag(0, 'ENTITY'), # First subclass tag should be ignored 234 DXFTag(3, 'X'), # callback value 235 DXFTag(3, 'Y'), # none callback value 236 ]) 237 ns, unprocessed_tags = load_tags_fast(cls, subclass, data) 238 assert ns.none_callback == 'Y' 239 assert ns.hasattr('callback') is False 240 241 242def test_if_fast_load_handles_unprocessed_duplicate_group_codes(cls, subclass): 243 data = Tags([ 244 DXFTag(0, 'ENTITY'), # First subclass tag should be ignored 245 DXFTag(1, '1'), # duplicate group code 246 DXFTag(1, '3'), # duplicate group code 247 DXFTag(1, '5'), # duplicate group code, but without an attribute definition 248 ]) 249 ns, unprocessed_tags = load_tags_fast(cls, subclass, data) 250 assert ns.test1 == '1' 251 assert ns.test3 == '3' 252 # only two group code 1 attributes are defined: 253 assert len(unprocessed_tags) == 1 254 assert unprocessed_tags[0] == (1, '5') 255 256 257TEST_1 = """0 258DXFENTITY 2595 260FFFF 261330 262ABBA 263""" 264 265if __name__ == '__main__': 266 pytest.main([__file__]) 267