1# Created: 30.04.2011, 2018 rewritten for pytest 2# Copyright (C) 2011-2019, Manfred Moitzi 3# License: MIT License 4import pytest 5from io import StringIO 6 7from ezdxf.lldxf.tags import Tags, DXFTag 8from ezdxf.lldxf.extendedtags import ExtendedTags 9from ezdxf import DXFKeyError, DXFValueError 10from ezdxf.lldxf.tagwriter import TagWriter 11 12 13@pytest.fixture 14def xtags1(): 15 return ExtendedTags.from_text(XTAGS1) 16 17 18def test_init_appdata(xtags1): 19 assert xtags1.get_app_data('{ACAD_XDICTIONARY') is not None 20 21 22def test_init_with_tags(): 23 tags = Tags.from_text(XTAGS1) 24 xtags = ExtendedTags(tags) 25 assert 3 == len(xtags.subclasses) 26 assert 1 == len(xtags.xdata) 27 28 29def test_init_xdata(xtags1): 30 assert xtags1.get_xdata('RAK') is not None 31 32 33def test_init_one_tag(): 34 xtags = ExtendedTags([DXFTag(0, 'SECTION')]) 35 assert xtags.noclass[0] == (0, 'SECTION') 36 37 38def test_getitem(xtags1): 39 assert xtags1[0] == xtags1.noclass[0] 40 41 42def test_appdata_content_count(xtags1): 43 xdict = xtags1.get_app_data('{ACAD_XDICTIONARY') 44 assert 3 == len(xdict) 45 46 47def test_appdata_content(xtags1): 48 xdict = xtags1.get_app_data('{ACAD_XDICTIONARY') 49 assert xdict.get_first_value(360) == "63D5" 50 51 52def test_tags_skips_appdata_content(xtags1): 53 with pytest.raises(DXFValueError): 54 xtags1.noclass.get_first_value(360) 55 56 57def test_xdata_content_count(xtags1): 58 rak = xtags1.get_xdata('RAK') 59 assert 17 == len(rak) 60 61 62def test_tags_skips_xdata_content(xtags1): 63 with pytest.raises(DXFValueError): 64 xtags1.noclass.get_first_value(1000) 65 66 67def test_copy(xtags1): 68 stream = StringIO() 69 tagwriter = TagWriter(stream) 70 tagwriter.write_tags(xtags1) 71 assert XTAGS1 == stream.getvalue() 72 stream.close() 73 74 75def test_getitem_layer(xtags1): 76 assert xtags1.noclass[0] == (0, 'LAYER') 77 78 79def test_getitem_xdict(xtags1): 80 assert xtags1.noclass[2] == (102, 0) 81 82 83def test_getitem_parent(xtags1): 84 assert xtags1.noclass[3] == (330, '18') 85 86 87def test_get_last_item(xtags1): 88 assert xtags1.noclass[-1] == (330, '18') 89 90 91def test_tagscount(xtags1): 92 """ apdata counts as one tag and xdata counts as one tag. """ 93 assert 4 == len(xtags1.noclass) 94 95 96def test_subclass_AcDbSymbolTableRecord(xtags1): 97 subclass = xtags1.get_subclass('AcDbSymbolTableRecord') 98 assert 1 == len(subclass) 99 100 101def test_subclass_AcDbLayerTableRecord(xtags1): 102 subclass = xtags1.get_subclass('AcDbLayerTableRecord') 103 assert 8 == len(subclass) 104 105 106def test_clone_is_equal(xtags1): 107 clone = xtags1.clone() 108 assert xtags1 is not clone 109 assert xtags1.appdata is not clone.appdata 110 assert xtags1.subclasses is not clone.subclasses 111 assert xtags1.xdata is not clone.xdata 112 assert list(xtags1) == list(clone) 113 114 115def test_replace_handle(xtags1): 116 xtags1.replace_handle('AA') 117 assert 'AA' == xtags1.get_handle() 118 119 120XTAGS1 = """ 0 121LAYER 122 5 1237 124102 125{ACAD_XDICTIONARY 126360 12763D5 128102 129} 130330 13118 132100 133AcDbSymbolTableRecord 134100 135AcDbLayerTableRecord 136 2 1370 138 70 1390 140 62 1417 142 6 143CONTINUOUS 144370 145-3 146390 1478 148347 149805 1501001 151RAK 1521000 153{75-LÄNGENSCHNITT-14 1541070 1550 1561070 1577 1581000 159CONTINUOUS 1601071 161-3 1621071 1631 1641005 1658 1661000 16775-LÄNGENSCHNITT-14} 1681000 169{75-LÄNGENSCHNITT-2005 1701070 1710 1721070 1737 1741000 175CONTINUOUS 1761071 177-3 1781071 1791 1801005 1818 1821000 18375-LÄNGENSCHNITT-2005} 184""" 185 186 187@pytest.fixture 188def xtags2(): 189 return ExtendedTags.from_text(XTAGS2) 190 191 192def test_xdata_count(xtags2): 193 assert 3 == len(xtags2.xdata) 194 195 196def test_tags_count(xtags2): 197 """ 3 xdata chunks and two 'normal' tag. """ 198 assert 2 == len(xtags2.noclass) 199 200 201def test_xdata3_tags(xtags2): 202 xdata = xtags2.get_xdata('XDATA3') 203 assert xdata[0] == (1001, 'XDATA3') 204 assert xdata[1] == (1000, 'TEXT-XDATA3') 205 assert xdata[2] == (1070, 2) 206 assert xdata[3] == (1070, 3) 207 208 209def test_new_data(xtags2): 210 xtags2.new_xdata('NEWXDATA', [(1000, 'TEXT')]) 211 assert xtags2.has_xdata('NEWXDATA') is True 212 213 xdata = xtags2.get_xdata('NEWXDATA') 214 assert xdata[0] == (1001, 'NEWXDATA') 215 assert xdata[1] == (1000, 'TEXT') 216 217 218def test_set_new_data(xtags2): 219 xtags2.new_xdata('NEWXDATA', tags=[(1000, "Extended Data String")]) 220 assert xtags2.has_xdata('NEWXDATA') is True 221 222 xdata = xtags2.get_xdata('NEWXDATA') 223 assert (1001, 'NEWXDATA') == xdata[0] 224 assert (1000, "Extended Data String") == xdata[1] 225 226 227def test_append_xdata(xtags2): 228 xdata = xtags2.get_xdata('MOZMAN') 229 assert 4 == len(xdata) 230 231 xdata.append(DXFTag(1000, "Extended Data String")) 232 xdata = xtags2.get_xdata('MOZMAN') 233 assert 5 == len(xdata) 234 235 assert DXFTag(1000, "Extended Data String") == xdata[4] 236 237 238XTAGS2 = """ 0 239LAYER 240 5 2417 2421001 243RAK 2441000 245TEXT-RAK 2461070 2471 2481070 2491 2501001 251MOZMAN 2521000 253TEXT-MOZMAN 2541070 2552 2561070 2572 2581001 259XDATA3 2601000 261TEXT-XDATA3 2621070 2632 2641070 2653 266""" 267 268 269@pytest.fixture 270def xtags3(): 271 return ExtendedTags.from_text(SPECIALCASE_TEXT) 272 273 274def test_read_tags(xtags3): 275 subclass2 = xtags3.get_subclass('AcDbText') 276 assert (100, 'AcDbText') == subclass2[0] 277 278 279def test_read_tags_2(xtags3): 280 subclass2 = xtags3.get_subclass('AcDbText') 281 assert (100, 'AcDbText') == subclass2[0] 282 assert (1, 'Title:') == subclass2[3] 283 284 285def test_read_tags_3(xtags3): 286 subclass2 = xtags3.get_subclass('AcDbText', 3) 287 assert (100, 'AcDbText') == subclass2[0] 288 assert (73, 2) == subclass2[1] 289 290 291def test_key_error(xtags3): 292 with pytest.raises(DXFKeyError): 293 xtags3.get_subclass('AcDbText', pos=4) 294 295 296def test_skip_empty_subclass(xtags3): 297 xtags3.subclasses[1] = Tags() # create empty subclass 298 subclass2 = xtags3.get_subclass('AcDbText') 299 assert (100, 'AcDbText') == subclass2[0] 300 301 302SPECIALCASE_TEXT = """ 0 303TEXT 3045 3058C9 306330 3076D 308100 309AcDbEntity 3108 3110 312100 313AcDbText 31410 3154.30 31620 3171.82 31830 3190.0 32040 3210.125 3221 323Title: 32441 3250.85 3267 327ARIALNARROW 328100 329AcDbText 33073 3312 332""" 333 334ACAD_REACTORS = '{ACAD_REACTORS' 335 336 337@pytest.fixture 338def xtags4(): 339 return ExtendedTags.from_text(NO_REACTORS) 340 341 342def test_get_not_existing_reactor(xtags4): 343 with pytest.raises(DXFValueError): 344 xtags4.get_app_data(ACAD_REACTORS) 345 346 347def test_new_reactors(xtags4): 348 xtags4.new_app_data(ACAD_REACTORS) 349 assert (102, 0) == xtags4.noclass[-1] # code = 102, value = index in appdata list 350 351 352def test_append_not_existing_reactors(xtags4): 353 xtags4.new_app_data(ACAD_REACTORS, [DXFTag(330, 'DEAD')]) 354 reactors = xtags4.get_app_data_content(ACAD_REACTORS) 355 assert 1 == len(reactors) 356 assert DXFTag(330, 'DEAD') == reactors[0] 357 358 359def test_append_to_existing_reactors(xtags4): 360 xtags4.new_app_data(ACAD_REACTORS, [DXFTag(330, 'DEAD')]) 361 reactors = xtags4.get_app_data_content(ACAD_REACTORS) 362 reactors.append(DXFTag(330, 'DEAD2')) 363 xtags4.set_app_data_content(ACAD_REACTORS, reactors) 364 365 reactors = xtags4.get_app_data_content(ACAD_REACTORS) 366 assert DXFTag(330, 'DEAD') == reactors[0] 367 assert DXFTag(330, 'DEAD2') == reactors[1] 368 369 370NO_REACTORS = """ 0 371TEXT 372 5 3738C9 374330 3756D 376100 377AcDbEntity 378 8 3790 380100 381AcDbText 382 10 3834.30 384 20 3851.82 386 30 3870.0 388 40 3890.125 390 1 391Title: 392 41 3930.85 394 7 395ARIALNARROW 396""" 397 398 399def test_legacy_mode(): 400 """ Legacy mode does the same job as filter_subclass_markers(). """ 401 tags = ExtendedTags.from_text(LEICA_DISTO_TAGS, legacy=True) 402 assert 9 == len(tags.noclass) 403 assert 1 == len(tags.subclasses) 404 assert tags.noclass[0] == (0, 'LINE') 405 assert tags.noclass[1] == (8, 'LEICA_DISTO_3D') 406 assert tags.noclass[-1] == (210, (0, 0, 1)) 407 408 409LEICA_DISTO_TAGS = """0 410LINE 411100 412AcDbEntity 4138 414LEICA_DISTO_3D 41562 416256 4176 418ByLayer 4195 42075 421100 422AcDbLine 42310 4240.819021 42520 426-0.633955 42730 428-0.273577 42911 4300.753216 43121 432-0.582009 43331 434-0.276937 43539 4360 437210 4380 439220 4400 441230 4421 443""" 444 445 446def test_group_code_1000_outside_XDATA(): 447 tags = ExtendedTags(Tags.from_text(BLOCKBASEPOINTPARAMETER_CVIL_3D_2018)) 448 assert tags.dxftype() == 'BLOCKBASEPOINTPARAMETER' 449 assert len(tags.subclasses) == 6 450 block_base_point_parameter = tags.get_subclass('AcDbBlockBasepointParameter') 451 assert len(block_base_point_parameter) == 3 452 assert block_base_point_parameter[0] == (100, 'AcDbBlockBasepointParameter') 453 assert block_base_point_parameter[1] == (1011, (0., 0., 0.)) 454 assert block_base_point_parameter[2] == (1012, (0., 0., 0.)) 455 456 block_element = tags.get_subclass('AcDbBlockElement') 457 assert block_element[4] == (1071, 0) 458 459 stream = StringIO() 460 tagwriter = TagWriter(stream) 461 tagwriter.write_tags(tags) 462 lines = stream.getvalue() 463 stream.close() 464 assert len(lines.split('\n')) == len(BLOCKBASEPOINTPARAMETER_CVIL_3D_2018.split('\n')) 465 466 467BLOCKBASEPOINTPARAMETER_CVIL_3D_2018 = """0 468BLOCKBASEPOINTPARAMETER 4695 4704C25 471330 4724C23 473100 474AcDbEvalExpr 47590 4761 47798 47833 47999 4804 481100 482AcDbBlockElement 483300 484Base Point 48598 48633 48799 4884 4891071 4900 491100 492AcDbBlockParameter 493280 4941 495281 4960 497100 498AcDbBlock1PtParameter 4991010 500-3.108080399920343 5011020 502-0.9562299080084814 5031030 5040.0 50593 5060 507170 5080 509171 5100 511100 512AcDbBlockBasepointParameter 5131011 5140.0 5151021 5160.0 5171031 5180.0 5191012 5200.0 5211022 5220.0 5231032 5240.0 525""" 526 527 528def test_xrecord_with_group_code_102(): 529 tags = ExtendedTags(Tags.from_text(XRECORD_WITH_GROUP_CODE_102)) 530 assert tags.dxftype() == 'XRECORD' 531 assert len(tags.appdata) == 1 532 assert tags.noclass[2] == (102, 0) # 0 == index in appdata list 533 assert tags.appdata[0][0] == (102, '{ACAD_REACTORS') 534 535 xrecord = tags.get_subclass('AcDbXrecord') 536 assert xrecord[2] == (102, 'ACAD_ROUNDTRIP_PRE2007_TABLESTYLE') 537 assert len(list(tags)) * 2 + 1 == len(XRECORD_WITH_GROUP_CODE_102.split('\n')) # +1 == appending '\n' 538 539 540XRECORD_WITH_GROUP_CODE_102 = """0 541XRECORD 5425 543D9B071D01A0CB6A5 544102 545{ACAD_REACTORS 546330 547D9B071D01A0CB69D 548102 549} 550330 551D9B071D01A0CB69D 552100 553AcDbXrecord 554280 555 1 556102 557ACAD_ROUNDTRIP_PRE2007_TABLESTYLE 55890 559 4 56091 561 0 5621 563 56492 565 4 56693 567 0 5682 569 57094 571 4 57295 573 0 5743 575 576""" 577 578 579def test_xrecord_with_long_closing_tag(): 580 tags = ExtendedTags(Tags.from_text(XRECORD_APP_DATA_LONG_CLOSING_TAG)) 581 assert tags.dxftype() == 'XRECORD' 582 # real app data just exists only in the base class, app data marker in AcDbXrecord are just tags, interpreted by 583 # the associated application 584 assert len(tags.appdata) == 1 585 assert len(tags.subclasses[1]) == 35 586 587 588XRECORD_APP_DATA_LONG_CLOSING_TAG = """ 0 589XRECORD 5905 5912F9 592102 593{ACAD_REACTORS 594330 5952FF 596102 597} 598330 5992FF 600100 601AcDbXrecord 602280 6031 6041 605AcDb_Thumbnail_Schema 606102 607{ATTRRECORD 608341 6092FA 610102 611USUAL_102_TAG_INSIDE_APP_DATA 6122 613AcDbDs::TreatedAsObjectData 614280 6151 616291 6171 618102 619ATTRRECORD} 620102 621USUAL_102_TAG_OUTSIDE_APP_DATA 622102 623{ATTRRECORD 624341 6252FB 6262 627AcDbDs::Legacy 628280 6291 630291 6311 632102 633ATTRRECORD} 6342 635AcDbDs::ID 636280 63710 63891 6398 640102 641{ATTRRECORD 642341 6432FC 6442 645AcDs:Indexable 646280 6471 648291 6491 650102 651ATTRRECORD} 652102 653{ATTRRECORD 654341 6552FD 6562 657AcDbDs::HandleAttribute 658280 6597 660282 6611 662102 663ATTRRECORD} 6642 665Thumbnail_Data 666280 66715 66891 6690 670""" 671