mirror of
https://github.com/qelectrotech/qelectrotech-source-mirror.git
synced 2026-10-06 11:14:14 +02:00
Merge branch 'master' into fix/pdf-landscape-paper-size
This commit is contained in:
@@ -104,6 +104,13 @@ add_test(NAME tst_textgrid COMMAND tst_textgrid)
|
||||
target_include_directories(tst_textgrid PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_textgrid PRIVATE Qt::Test)
|
||||
|
||||
# elementviewgrid.h is header-only: the symbol editor's snap step at each
|
||||
# zoom (bugtracker #112), tested without an editor window.
|
||||
add_executable(tst_elementviewgrid tst_elementviewgrid.cpp)
|
||||
add_test(NAME tst_elementviewgrid COMMAND tst_elementviewgrid)
|
||||
target_include_directories(tst_elementviewgrid PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_elementviewgrid PRIVATE Qt::Test)
|
||||
|
||||
# alignment.h is header-only too (it builds on textgrid.h): the geometry
|
||||
# behind the align commands, tested without a scene.
|
||||
add_executable(tst_alignment tst_alignment.cpp)
|
||||
@@ -400,6 +407,19 @@ target_link_libraries(tst_pdfpagesize PRIVATE Qt::Test Qt::Gui)
|
||||
target_compile_definitions(tst_pdfpagesize PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# One text per potential: the number goes on the same conductor in every
|
||||
# run when two conductors tie for longest. Exports
|
||||
# fixtures/one_text_per_potential_tie.qet through the real binary, several
|
||||
# times, since the order it used to depend on changes only between runs.
|
||||
add_executable(
|
||||
tst_potentialtextcarrier
|
||||
tst_potentialtextcarrier.cpp)
|
||||
add_test(NAME tst_potentialtextcarrier COMMAND tst_potentialtextcarrier)
|
||||
add_dependencies(tst_potentialtextcarrier qelectrotech)
|
||||
target_link_libraries(tst_potentialtextcarrier PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_potentialtextcarrier PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# Hops where wires cross (issue #436): the geometry in wirehops.cpp, and
|
||||
# through the real binary, that --resave keeps the project setting and
|
||||
# adds nothing to a project without it.
|
||||
@@ -414,6 +434,16 @@ target_link_libraries(tst_wirehops PRIVATE Qt::Test Qt::Gui)
|
||||
target_compile_definitions(tst_wirehops PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\"")
|
||||
|
||||
# The routing behind qet.routeConductor(): conductorrouter.cpp needs only
|
||||
# Qt Core, so the geometry is tested without a scene.
|
||||
add_executable(
|
||||
tst_conductorrouter
|
||||
tst_conductorrouter.cpp
|
||||
${QET_DIR}/sources/conductorrouter.cpp)
|
||||
add_test(NAME tst_conductorrouter COMMAND tst_conductorrouter)
|
||||
target_include_directories(tst_conductorrouter PRIVATE ${QET_DIR}/sources)
|
||||
target_link_libraries(tst_conductorrouter PRIVATE Qt::Test)
|
||||
|
||||
# qet.conductorUuids() / qet.conductorEnds(): a script lists a folio's
|
||||
# conductors by uuid and finds each one's two ends. Runs a script through
|
||||
# the real binary's --run on fixtures/qet_bug_repro_resaved.qet, so only
|
||||
@@ -441,6 +471,18 @@ if(QET_HAS_SCRIPTING)
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_ELEMENTS_DIR=\"${QET_DIR}/elements\"")
|
||||
|
||||
# qet.addElement() on a symbol that exists but cannot be read says so,
|
||||
# rather than reporting a collision with the copy already embedded.
|
||||
add_executable(
|
||||
tst_unreadableelement
|
||||
tst_unreadableelement.cpp)
|
||||
add_test(NAME tst_unreadableelement COMMAND tst_unreadableelement)
|
||||
add_dependencies(tst_unreadableelement qelectrotech)
|
||||
target_link_libraries(tst_unreadableelement PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_unreadableelement PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_ELEMENTS_DIR=\"${QET_DIR}/elements\"")
|
||||
|
||||
# The project database filled from the file as it is read holds exactly
|
||||
# what it holds filled from the built folios (QET_DATABASE_FROM_FOLIOS=1),
|
||||
# on every example saved once; an older file falls back, saying why.
|
||||
@@ -479,6 +521,19 @@ if(QET_HAS_SCRIPTING)
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\"")
|
||||
|
||||
# The same symbol placed twice in a project whose collection does not
|
||||
# start with its "import" category (examples/lmdg.qet).
|
||||
add_executable(
|
||||
tst_importcategory
|
||||
tst_importcategory.cpp)
|
||||
add_test(NAME tst_importcategory COMMAND tst_importcategory)
|
||||
add_dependencies(tst_importcategory qelectrotech)
|
||||
target_link_libraries(tst_importcategory PRIVATE Qt::Test)
|
||||
target_compile_definitions(tst_importcategory PRIVATE
|
||||
"QET_TEST_BINARY_PATH=\"$<TARGET_FILE:qelectrotech>\""
|
||||
"QET_EXAMPLES_DIR=\"${QET_DIR}/examples\""
|
||||
"QET_ELEMENTS_DIR=\"${QET_DIR}/elements\"")
|
||||
|
||||
# qet.currentFolio() through --run: the first folio, or -1 with none; and
|
||||
# --run keeps one undo step per call (only an editor run is grouped).
|
||||
add_executable(
|
||||
|
||||
@@ -0,0 +1,320 @@
|
||||
<project title="one text per potential, tied lengths" uuid="{74544bb0-3f71-58af-9052-eb46f8a03dfd}" version="0.200.1">
|
||||
<properties>
|
||||
<property name="saveddate" show="1">10/2/26</property>
|
||||
<property name="saveddate-eu" show="1">02-10-2026</property>
|
||||
<property name="saveddate-us" show="1">2026-10-02</property>
|
||||
<property name="savedfilename" show="1">one_text_per_potential_tie</property>
|
||||
<property name="savedfilepath" show="1">one_text_per_potential_tie.qet</property>
|
||||
<property name="savedtime" show="1">22:07</property>
|
||||
</properties>
|
||||
<usage enabled="true" time_spent="0"/>
|
||||
<newdiagrams>
|
||||
<border cols="17" colsize="60" displaycols="true" displayrows="true" rows="8" rowsize="80"/>
|
||||
<inset author="" auto_page_num="" date="null" displayAt="bottom" filename="" folio="" indexrev="" locmach="" plant="" title="" version=""/>
|
||||
<conductors bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="0" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<report label="%f-%l%c"/>
|
||||
<xrefs>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="coil" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="commutator" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="plc" xrefpos="AlignBottom"/>
|
||||
<xref delayprefix="" displayhas="cross" master_label="%f-%l%c" offset="0" powerprefix="" showallconfiguredslaves="false" showpowerctc="true" showterminalname="true" slave_label="(%f-%l%c)" slave_offset="0" snapto="label" switchprefix="" type="protection" xrefpos="AlignBottom"/>
|
||||
</xrefs>
|
||||
<conductors_autonums auto_break_conductors="false" current_autonum="" freeze_new_conductors="false"/>
|
||||
<folio_autonums/>
|
||||
<element_autonums current_autonum="" freeze_new_elements="false"/>
|
||||
<guides/>
|
||||
</newdiagrams>
|
||||
<diagram author="" auto_page_num="" cols="17" colsize="60" date="null" displayAt="bottom" displaycols="true" displayrows="true" filename="" folio="" freezeNewConductor="false" freezeNewElement="false" height="660" indexrev="" locmach="" order="1" plant="" rows="8" rowsize="80" title="" uuid="{91957ff3-7b74-4012-a876-f858c68b2d60}" version="0.200.1-dev">
|
||||
<defaultconductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" formula="" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="_" numsize="7" onetextperfolio="1" tension_protocol="" text_color="#000000" type="multi" vertical-alignment="AlignRight" vertirotatetext="270"/>
|
||||
<elements>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="R" type="embed://import/10_electric/10_allpole/395_electronics_semiconductors/01_resistors/resistance.elmt" uuid="{3817b0a1-cab7-454e-81f2-7678b242cced}" x="100" y="300" z="10">
|
||||
<terminals>
|
||||
<terminal id="0" orientation="0" x="0" y="-16"/>
|
||||
<terminal id="1" orientation="2" x="0" y="16"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{1a7d317c-51b7-402f-807e-79aa136df233}" x="6" y="-11.5">
|
||||
<text></text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="R" type="embed://import/10_electric/10_allpole/395_electronics_semiconductors/01_resistors/resistance.elmt" uuid="{f571bdba-9562-4119-8a18-0d798ebb7c3d}" x="300" y="100" z="10">
|
||||
<terminals>
|
||||
<terminal id="2" orientation="0" x="0" y="-16"/>
|
||||
<terminal id="3" orientation="2" x="0" y="16"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{79099d69-dcec-4247-bd03-cdb07a0194d6}" x="6" y="-11.5">
|
||||
<text></text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="R" type="embed://import/10_electric/10_allpole/395_electronics_semiconductors/01_resistors/resistance.elmt" uuid="{c05fe3f1-f4a1-4beb-843b-90aadc3ca6c0}" x="300" y="500" z="10">
|
||||
<terminals>
|
||||
<terminal id="4" orientation="0" x="0" y="-16"/>
|
||||
<terminal id="5" orientation="2" x="0" y="16"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{075df885-82c2-49dd-817d-809137a3329d}" x="6" y="-11.5">
|
||||
<text></text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
<element freezeLabel="false" is_movable="1" orientation="0" prefix="R" type="embed://import/10_electric/10_allpole/395_electronics_semiconductors/01_resistors/resistance.elmt" uuid="{d0acb0f6-8c2f-4daa-8365-7ad1df6f7f86}" x="400" y="300" z="10">
|
||||
<terminals>
|
||||
<terminal id="6" orientation="0" x="0" y="-16"/>
|
||||
<terminal id="7" orientation="2" x="0" y="16"/>
|
||||
</terminals>
|
||||
<inputs/>
|
||||
<dynamic_texts>
|
||||
<dynamic_elmt_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" rotation_point_center="false" text_from="ElementInfo" text_width="-1" uuid="{11eafa99-e5a9-4baf-a2ed-8469780c7fa8}" x="6" y="-11.5">
|
||||
<text></text>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_elmt_text>
|
||||
</dynamic_texts>
|
||||
<texts_groups/>
|
||||
</element>
|
||||
</elements>
|
||||
<conductors>
|
||||
<conductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{3817b0a1-cab7-454e-81f2-7678b242cced}" element1_label="" element1_name="Resistor" element2="{f571bdba-9562-4119-8a18-0d798ebb7c3d}" element2_label="" element2_name="Resistor" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="42" numsize="7" onetextperfolio="1" tension_protocol="" terminal1="{09a3060b-a0fd-41ad-b605-f5c0ae63901e}" terminal2="{4410c57c-3796-4696-b63b-d8d5a0c9b788}" terminalname1="A2" terminalname2="A1" text_color="#000000" type="multi" uuid="{26591715-c028-44c9-bf2d-c828f8187527}" vertical-alignment="AlignRight" vertirotatetext="270" x="0" y="0">
|
||||
<sequentialNumbers/>
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{f571bdba-9562-4119-8a18-0d798ebb7c3d}" element1_label="" element1_name="Resistor" element2="{c05fe3f1-f4a1-4beb-843b-90aadc3ca6c0}" element2_label="" element2_name="Resistor" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="42" numsize="7" onetextperfolio="1" tension_protocol="" terminal1="{4410c57c-3796-4696-b63b-d8d5a0c9b788}" terminal2="{09a3060b-a0fd-41ad-b605-f5c0ae63901e}" terminalname1="A1" terminalname2="A2" text_color="#000000" type="multi" uuid="{0c9e2a9a-f402-49f4-9780-a8c72704acff}" vertical-alignment="AlignRight" vertirotatetext="270" x="0" y="0">
|
||||
<sequentialNumbers/>
|
||||
</conductor>
|
||||
<conductor bicolor="false" bus="" cable="" color2="#000000" condsize="1" conductor_color="" conductor_section="" dash-size="1" displaytext="1" element1="{f571bdba-9562-4119-8a18-0d798ebb7c3d}" element1_label="" element1_name="Resistor" element2="{d0acb0f6-8c2f-4daa-8365-7ad1df6f7f86}" element2_label="" element2_name="Resistor" formula="" freezeLabel="false" function="" horizontal-alignment="AlignBottom" horizrotatetext="0" num="42" numsize="7" onetextperfolio="1" tension_protocol="" terminal1="{4410c57c-3796-4696-b63b-d8d5a0c9b788}" terminal2="{09a3060b-a0fd-41ad-b605-f5c0ae63901e}" terminalname1="A1" terminalname2="A2" text_color="#000000" type="multi" uuid="{5ecce027-3b6b-4e2e-9d30-f5dd59b320c0}" vertical-alignment="AlignRight" vertirotatetext="270" x="0" y="0">
|
||||
<sequentialNumbers/>
|
||||
</conductor>
|
||||
</conductors>
|
||||
</diagram>
|
||||
<collection>
|
||||
<category name="import">
|
||||
<names>
|
||||
<name lang="ca">Elements importats</name>
|
||||
<name lang="cs">Zavedené prvky</name>
|
||||
<name lang="da">Importerede elementer</name>
|
||||
<name lang="de">Importierte Elemente</name>
|
||||
<name lang="el">Εισηγμένα στοιχεία</name>
|
||||
<name lang="en">Imported elements</name>
|
||||
<name lang="es">Elementos importados</name>
|
||||
<name lang="fr">Éléments importés</name>
|
||||
<name lang="hr">Uvezeni elementi</name>
|
||||
<name lang="it">Elementi importati</name>
|
||||
<name lang="ja">インバートされた要素</name>
|
||||
<name lang="ko">전기 심볼 요소</name>
|
||||
<name lang="ko_KR">전기 심볼 요소</name>
|
||||
<name lang="nl">Elementen geïmporteerd</name>
|
||||
<name lang="nl_BE">Elementen geïmporteerd</name>
|
||||
<name lang="pl">Elementy importowane</name>
|
||||
<name lang="pt">Elementos importados</name>
|
||||
<name lang="pt_BR">Elementos importados</name>
|
||||
<name lang="ro">Elemente importate</name>
|
||||
<name lang="ru">Импортированные элементы</name>
|
||||
<name lang="sl">Uvoženi elementi</name>
|
||||
<name lang="sv">Importerade element</name>
|
||||
<name lang="tr">İthal öğeler</name>
|
||||
<name lang="uk">Імпортовані елементи</name>
|
||||
<name lang="zh">导入元件</name>
|
||||
</names>
|
||||
<category name="10_electric">
|
||||
<names>
|
||||
<name lang="ar">كهربائي</name>
|
||||
<name lang="ca">Elèctric</name>
|
||||
<name lang="cs">Elektrotechnika</name>
|
||||
<name lang="da">Elektrisk</name>
|
||||
<name lang="de">Elektrik</name>
|
||||
<name lang="el">Ηλεκτρικά</name>
|
||||
<name lang="en">Electric</name>
|
||||
<name lang="es">Eléctrica</name>
|
||||
<name lang="fi">Sähkö</name>
|
||||
<name lang="fr">Electrique</name>
|
||||
<name lang="hr">Električno</name>
|
||||
<name lang="hu">Elektromos</name>
|
||||
<name lang="it">Elettrica</name>
|
||||
<name lang="ja">電気</name>
|
||||
<name lang="ko">전기</name>
|
||||
<name lang="mn">Цахилгаан</name>
|
||||
<name lang="nb">Elektrisk</name>
|
||||
<name lang="nl">Elektrotechniek</name>
|
||||
<name lang="nl_BE">Elektrisch</name>
|
||||
<name lang="pl">Elektrotechnika</name>
|
||||
<name lang="pt">Elétrico</name>
|
||||
<name lang="pt_BR">Elétrica</name>
|
||||
<name lang="ro">Electric</name>
|
||||
<name lang="ru">Электротехника</name>
|
||||
<name lang="sk">Elektrické</name>
|
||||
<name lang="sl">Elektrika</name>
|
||||
<name lang="sr">Електрика</name>
|
||||
<name lang="sv">El</name>
|
||||
<name lang="tr">Elektrik</name>
|
||||
<name lang="uk">Електрика</name>
|
||||
<name lang="zh">10_电气</name>
|
||||
<name lang="zn">电气工程</name>
|
||||
</names>
|
||||
<category name="10_allpole">
|
||||
<names>
|
||||
<name lang="ar">متعدد الأقطاب</name>
|
||||
<name lang="ca">Multifilar</name>
|
||||
<name lang="cs">Vícežilový</name>
|
||||
<name lang="da">Flere ledere</name>
|
||||
<name lang="de">Allpolig</name>
|
||||
<name lang="el">Πολυγραμμικό</name>
|
||||
<name lang="en">All-pole</name>
|
||||
<name lang="es">Multifilar</name>
|
||||
<name lang="fi">Yleisnapa</name>
|
||||
<name lang="fr">Multifilaire</name>
|
||||
<name lang="hr">Univerzalni</name>
|
||||
<name lang="hu">Többvonalas szimbólumok</name>
|
||||
<name lang="it">Multifilare</name>
|
||||
<name lang="ja">複線</name>
|
||||
<name lang="ko">다-극</name>
|
||||
<name lang="mn">Бүх туйл</name>
|
||||
<name lang="nb">Alle poler</name>
|
||||
<name lang="nl">Veel polig</name>
|
||||
<name lang="nl_BE">All-pole</name>
|
||||
<name lang="pl">Schematy wieloliniowe</name>
|
||||
<name lang="pt">Multipolar</name>
|
||||
<name lang="pt_BR">Multifilar</name>
|
||||
<name lang="ro">Bipolar</name>
|
||||
<name lang="ru">Многополюсные</name>
|
||||
<name lang="sk">Všepólové</name>
|
||||
<name lang="sl">Vsi poli</name>
|
||||
<name lang="sr">Свe-полни</name>
|
||||
<name lang="sv">Allpolig</name>
|
||||
<name lang="tr">Çok kutuplu</name>
|
||||
<name lang="uk">Багатополюсні</name>
|
||||
<name lang="zh">10_全极</name>
|
||||
</names>
|
||||
<category name="395_electronics_semiconductors">
|
||||
<names>
|
||||
<name lang="ar">إلكترونيات وأشباه موصلات</name>
|
||||
<name lang="ca">Electrònica i semiconductors</name>
|
||||
<name lang="cs">Elektronika a polovodiče</name>
|
||||
<name lang="da">Elektronik og halvledere</name>
|
||||
<name lang="de">Elektronik und Halbleiter</name>
|
||||
<name lang="el">Ηλεκτρονικά και ημιαγωγοί</name>
|
||||
<name lang="en">Electronics and semiconductors</name>
|
||||
<name lang="es">Electrónica y semiconductores</name>
|
||||
<name lang="fi">Elektroniikka ja puolijohteet</name>
|
||||
<name lang="fr">Electronique et semi-conducteurs</name>
|
||||
<name lang="hr">Elektronika i poluvodiči</name>
|
||||
<name lang="hu">Elektronika és félvezetők</name>
|
||||
<name lang="it">Elettronica e semiconduttori</name>
|
||||
<name lang="ja">電子と半導体</name>
|
||||
<name lang="ko">전자 공학 및 반도체</name>
|
||||
<name lang="mn">Электроник ба хагас дамжуулагч</name>
|
||||
<name lang="nb">Elektronikk og halvledere</name>
|
||||
<name lang="nl">Electronica en halfgeleiders</name>
|
||||
<name lang="nl_BE">Elektronica en halfgeleiders</name>
|
||||
<name lang="pl">Elektronika i półprzewodniki</name>
|
||||
<name lang="pt">Eletrónica e semicondutores</name>
|
||||
<name lang="pt_BR">Eletrônica e semi-condutoues</name>
|
||||
<name lang="ro">Electronică și semiconductoare</name>
|
||||
<name lang="ru">Электроника и полупроводники</name>
|
||||
<name lang="sk">Elektronika a polovodiče</name>
|
||||
<name lang="sl">Elektronika in polprevodniki</name>
|
||||
<name lang="sr">Електроника и полупроводници</name>
|
||||
<name lang="sv">Elektronik och halvledare</name>
|
||||
<name lang="tr">Elektronik ve yarı iletkenler</name>
|
||||
<name lang="uk">Електроніка та напівпровідники</name>
|
||||
<name lang="zh">电子与半导体</name>
|
||||
</names>
|
||||
<category name="01_resistors">
|
||||
<names>
|
||||
<name lang="ar">مُقاومة</name>
|
||||
<name lang="ca">Resistències</name>
|
||||
<name lang="cs">Odpory</name>
|
||||
<name lang="da">Modstande</name>
|
||||
<name lang="de">Widerstände</name>
|
||||
<name lang="el">Αντιστάσεις</name>
|
||||
<name lang="en">Resistors</name>
|
||||
<name lang="es">Resistencias</name>
|
||||
<name lang="fi">Vastukset</name>
|
||||
<name lang="fr">Résistances</name>
|
||||
<name lang="hr">Otpornici</name>
|
||||
<name lang="hu">Ellenállások</name>
|
||||
<name lang="it">Resistori</name>
|
||||
<name lang="ja">抵抗器</name>
|
||||
<name lang="ko">저항</name>
|
||||
<name lang="mn">Эсэргүүцэл</name>
|
||||
<name lang="nb">Motstander</name>
|
||||
<name lang="nl">Weerstand</name>
|
||||
<name lang="nl_BE">Weerstanden</name>
|
||||
<name lang="pl">Rezystory</name>
|
||||
<name lang="pt">Resistência</name>
|
||||
<name lang="pt_BR">Resistores</name>
|
||||
<name lang="ro">Rezistoare</name>
|
||||
<name lang="ru">Резисторы</name>
|
||||
<name lang="sk">Rezistory</name>
|
||||
<name lang="sl">Upori</name>
|
||||
<name lang="sr">Отпорници</name>
|
||||
<name lang="sv">Motstånd</name>
|
||||
<name lang="tr">Dirençler</name>
|
||||
<name lang="uk">Резистори</name>
|
||||
<name lang="zh">电阻器</name>
|
||||
</names>
|
||||
<element name="resistance.elmt">
|
||||
<definition height="60" hotspot_x="10" hotspot_y="30" link_type="simple" type="element" version="0.100.0" width="20">
|
||||
<uuid uuid="{2becfcef-f6f6-6a63-ef49-65f12026c4f8}"/>
|
||||
<names>
|
||||
<name lang="ar">مُقاومة</name>
|
||||
<name lang="ca">Resistència</name>
|
||||
<name lang="cs">Odpor</name>
|
||||
<name lang="da">Modstand</name>
|
||||
<name lang="de">Widerstand</name>
|
||||
<name lang="el">Αντίσταση</name>
|
||||
<name lang="en">Resistor</name>
|
||||
<name lang="es">Resistencia</name>
|
||||
<name lang="fi">Vastus</name>
|
||||
<name lang="fr">Résistance</name>
|
||||
<name lang="hr">Otpornik</name>
|
||||
<name lang="hu">Ellenállás</name>
|
||||
<name lang="it">Resistore</name>
|
||||
<name lang="ja">抵抗器</name>
|
||||
<name lang="ko">저항</name>
|
||||
<name lang="mn">Резистор</name>
|
||||
<name lang="nb">Motstand</name>
|
||||
<name lang="nl">Weerstand</name>
|
||||
<name lang="nl_BE">Weerstand</name>
|
||||
<name lang="pl">Rezystor</name>
|
||||
<name lang="pt">Resistência</name>
|
||||
<name lang="pt_BR">Resistor</name>
|
||||
<name lang="ro">Rezistor</name>
|
||||
<name lang="ru">Резистор</name>
|
||||
<name lang="sk">Rezistor</name>
|
||||
<name lang="sl">Upor</name>
|
||||
<name lang="sr">Отпорник</name>
|
||||
<name lang="sv">Resistor</name>
|
||||
<name lang="tr">Direnç</name>
|
||||
<name lang="uk">Резистор</name>
|
||||
<name lang="zh">电阻</name>
|
||||
</names>
|
||||
<informations>EN 60617: 04-01-01</informations>
|
||||
<description>
|
||||
<line antialias="false" end1="none" end2="none" length1="1.5" length2="1.5" style="line-style:normal;line-weight:normal;filling:none;color:black" x1="0" x2="0" y1="20" y2="-20"/>
|
||||
<rect antialias="false" height="20" rx="0" ry="0" style="line-style:normal;line-weight:normal;filling:white;color:black" width="8" x="-4" y="-10"/>
|
||||
<dynamic_text Halignment="AlignLeft" Valignment="AlignTop" font="Liberation Sans,9,-1,5,50,0,0,0,0,0,Regular" frame="false" keep_visual_rotation="false" rotation="0" text_from="ElementInfo" text_width="-1" uuid="{570cd854-578d-4c4b-bd2a-3498353e46b6}" x="6" y="-11.5" z="7">
|
||||
<text/>
|
||||
<info_name>label</info_name>
|
||||
</dynamic_text>
|
||||
<terminal name="A2" orientation="s" type="Generic" uuid="{09a3060b-a0fd-41ad-b605-f5c0ae63901e}" x="0" y="20"/>
|
||||
<terminal name="A1" orientation="n" type="Generic" uuid="{4410c57c-3796-4696-b63b-d8d5a0c9b788}" x="0" y="-20"/>
|
||||
</description>
|
||||
</definition>
|
||||
</element>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</category>
|
||||
</collection>
|
||||
</project>
|
||||
@@ -0,0 +1,212 @@
|
||||
/*
|
||||
Copyright 2006-2026 The QElectroTech Team
|
||||
This file is part of QElectroTech.
|
||||
|
||||
QElectroTech is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
QElectroTech is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with QElectroTech. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include "conductorrouter.h"
|
||||
|
||||
#include <QtTest>
|
||||
|
||||
/**
|
||||
The geometry of ConductorRouter on its own: that a route is made of
|
||||
horizontal and vertical segments, leaves and enters each terminal in
|
||||
the terminal's direction, and goes around obstacles rather than
|
||||
through them.
|
||||
*/
|
||||
class TestConductorRouter : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
using Direction = ConductorRouter::Direction;
|
||||
|
||||
static bool crosses(const QList<QPointF> &points, const QRectF &r)
|
||||
{
|
||||
for (int i = 0; i + 1 < points.size(); ++i) {
|
||||
const QPointF a = points.at(i), b = points.at(i + 1);
|
||||
const QRectF seg = QRectF(a, b).normalized();
|
||||
const qreal left = qMax(seg.left(), r.left()), right = qMin(seg.right(), r.right());
|
||||
const qreal top = qMax(seg.top(), r.top()), bottom = qMin(seg.bottom(), r.bottom());
|
||||
// a segment is a degenerate rectangle: it crosses the
|
||||
// interior when it overlaps it on one axis and lies
|
||||
// strictly inside it on the other
|
||||
if (seg.width() == 0 && seg.left() > r.left() && seg.left() < r.right() && bottom > top)
|
||||
return true;
|
||||
if (seg.height() == 0 && seg.top() > r.top() && seg.top() < r.bottom() && right > left)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static void checkShape(const QList<QPointF> &p, const ConductorRouter::Request &r)
|
||||
{
|
||||
QVERIFY(p.size() >= 3);
|
||||
QCOMPARE(p.first(), r.start);
|
||||
QCOMPARE(p.last(), r.end);
|
||||
for (int i = 0; i + 1 < p.size(); ++i)
|
||||
QVERIFY2(p.at(i).x() == p.at(i + 1).x() || p.at(i).y() == p.at(i + 1).y(),
|
||||
"a segment is neither horizontal nor vertical");
|
||||
}
|
||||
|
||||
static QPointF unit(Direction d)
|
||||
{
|
||||
switch (d) {
|
||||
case Direction::North: return {0, -1};
|
||||
case Direction::East: return {1, 0};
|
||||
case Direction::South: return {0, 1};
|
||||
case Direction::West: return {-1, 0};
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static QPointF direction(QPointF from, QPointF to)
|
||||
{
|
||||
const QPointF d = to - from;
|
||||
return {d.x() > 0 ? 1. : d.x() < 0 ? -1. : 0., d.y() > 0 ? 1. : d.y() < 0 ? -1. : 0.};
|
||||
}
|
||||
|
||||
private slots:
|
||||
void straightWhenNothingIsInTheWay()
|
||||
{
|
||||
ConductorRouter::Request r;
|
||||
r.start = {100, 100};
|
||||
r.start_direction = Direction::East;
|
||||
r.end = {300, 100};
|
||||
r.end_direction = Direction::West;
|
||||
const auto result = ConductorRouter::route(r);
|
||||
checkShape(result.points, r);
|
||||
// no bend at all: start, the two exit points, end
|
||||
for (const QPointF &p : result.points) QCOMPARE(p.y(), 100.);
|
||||
}
|
||||
|
||||
void goesAroundASymbol()
|
||||
{
|
||||
ConductorRouter::Request r;
|
||||
r.start = {100, 100};
|
||||
r.start_direction = Direction::East;
|
||||
r.end = {300, 100};
|
||||
r.end_direction = Direction::West;
|
||||
const QRectF symbol(170, 60, 60, 80);
|
||||
r.obstacles << symbol;
|
||||
const auto result = ConductorRouter::route(r);
|
||||
QVERIFY2(result.error.isEmpty(), qPrintable(result.error));
|
||||
checkShape(result.points, r);
|
||||
QVERIFY(!crosses(result.points, symbol.adjusted(-r.margin, -r.margin, r.margin, r.margin)));
|
||||
}
|
||||
|
||||
// The test that the check above can fail: a straight line through
|
||||
// the symbol is reported as crossing it.
|
||||
void crossingCheckCanFail()
|
||||
{
|
||||
QVERIFY(crosses({{100, 100}, {300, 100}}, QRectF(170, 60, 60, 80)));
|
||||
}
|
||||
|
||||
void leavesAndEntersInTheTerminalsDirections_data()
|
||||
{
|
||||
QTest::addColumn<int>("from");
|
||||
QTest::addColumn<int>("to");
|
||||
for (int a = 0; a < 4; ++a)
|
||||
for (int b = 0; b < 4; ++b)
|
||||
QTest::addRow("%d-%d", a, b) << a << b;
|
||||
}
|
||||
|
||||
void leavesAndEntersInTheTerminalsDirections()
|
||||
{
|
||||
QFETCH(int, from);
|
||||
QFETCH(int, to);
|
||||
ConductorRouter::Request r;
|
||||
r.start = {100, 100};
|
||||
r.start_direction = Direction(from);
|
||||
r.end = {250, 180};
|
||||
r.end_direction = Direction(to);
|
||||
// each terminal's own symbol, on the side opposite the way
|
||||
// the terminal points
|
||||
const QPointF s = unit(r.start_direction), e = unit(r.end_direction);
|
||||
const QRectF own_start(r.start - s * 40 - QPointF(20, 20), QSizeF(40, 40));
|
||||
const QRectF own_end(r.end - e * 40 - QPointF(20, 20), QSizeF(40, 40));
|
||||
r.obstacles << own_start << own_end;
|
||||
const auto result = ConductorRouter::route(r);
|
||||
QVERIFY2(result.error.isEmpty(), qPrintable(result.error));
|
||||
checkShape(result.points, r);
|
||||
const auto &p = result.points;
|
||||
QCOMPARE(direction(p.at(0), p.at(1)), s);
|
||||
QCOMPARE(direction(p.at(p.size() - 1), p.at(p.size() - 2)), e);
|
||||
QVERIFY(!crosses(p, own_start));
|
||||
QVERIFY(!crosses(p, own_end));
|
||||
}
|
||||
|
||||
void prefersNotToRunAlongAnotherWire()
|
||||
{
|
||||
ConductorRouter::Request r;
|
||||
r.start = {100, 100};
|
||||
r.start_direction = Direction::East;
|
||||
r.end = {300, 100};
|
||||
r.end_direction = Direction::West;
|
||||
// a wire lying exactly on the straight route
|
||||
r.wires << QVector<QPointF>{{110, 100}, {290, 100}};
|
||||
const auto result = ConductorRouter::route(r);
|
||||
checkShape(result.points, r);
|
||||
bool on_it = false;
|
||||
for (int i = 1; i + 2 < result.points.size(); ++i)
|
||||
if (result.points.at(i).y() == 100 && result.points.at(i + 1).y() == 100)
|
||||
on_it = true;
|
||||
QVERIFY(!on_it);
|
||||
}
|
||||
|
||||
void staysOnTheFolio()
|
||||
{
|
||||
ConductorRouter::Request r;
|
||||
r.start = {100, 30};
|
||||
r.start_direction = Direction::East;
|
||||
r.end = {300, 30};
|
||||
r.end_direction = Direction::West;
|
||||
r.obstacles << QRectF(170, 0, 60, 200);
|
||||
r.bounds = QRectF(0, 0, 500, 400);
|
||||
const auto result = ConductorRouter::route(r);
|
||||
QVERIFY2(result.error.isEmpty(), qPrintable(result.error));
|
||||
for (const QPointF &p : result.points) QVERIFY(r.bounds.contains(p));
|
||||
}
|
||||
|
||||
void reportsWhenThereIsNoWay()
|
||||
{
|
||||
ConductorRouter::Request r;
|
||||
r.start = {100, 100};
|
||||
r.start_direction = Direction::East;
|
||||
r.end = {300, 100};
|
||||
r.end_direction = Direction::West;
|
||||
// a wall from the top of the folio to the bottom
|
||||
r.obstacles << QRectF(170, -10, 60, 520);
|
||||
r.bounds = QRectF(0, 0, 500, 500);
|
||||
const auto result = ConductorRouter::route(r);
|
||||
QVERIFY(result.points.isEmpty());
|
||||
QVERIFY(!result.error.isEmpty());
|
||||
}
|
||||
|
||||
void terminalsOffTheGrid()
|
||||
{
|
||||
ConductorRouter::Request r;
|
||||
r.start = {103, 97};
|
||||
r.start_direction = Direction::South;
|
||||
r.end = {287, 213};
|
||||
r.end_direction = Direction::North;
|
||||
r.obstacles << QRectF(120, 140, 200, 30);
|
||||
const auto result = ConductorRouter::route(r);
|
||||
QVERIFY2(result.error.isEmpty(), qPrintable(result.error));
|
||||
checkShape(result.points, r);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_MAIN(TestConductorRouter)
|
||||
#include "tst_conductorrouter.moc"
|
||||
@@ -65,6 +65,24 @@ private slots:
|
||||
QCOMPARE(fromDom(xml(value)), expected);
|
||||
QCOMPARE(fromPugi(xml(value)), expected);
|
||||
}
|
||||
|
||||
// remove() forgets the key entirely: a context with a key added
|
||||
// then removed equals one that never had it. remove() used to keep
|
||||
// the key's show flag, so the two never compared equal, and
|
||||
// Project properties > OK saw a change where there was none.
|
||||
void removeForgetsTheKey()
|
||||
{
|
||||
DiagramContext edited;
|
||||
edited.addValue(QStringLiteral("author"), QStringLiteral("N.V."));
|
||||
edited.addValue(QStringLiteral("01-numprojet"), QString());
|
||||
edited.remove(QStringLiteral("01-numprojet"));
|
||||
|
||||
DiagramContext stored;
|
||||
stored.addValue(QStringLiteral("author"), QStringLiteral("N.V."));
|
||||
|
||||
QCOMPARE(edited.keys(), stored.keys());
|
||||
QVERIFY(edited == stored);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_diagramcontext)
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <QtTest>
|
||||
|
||||
#include "editor/elementviewgrid.h"
|
||||
|
||||
// The symbol editor's snap step at each zoom (bugtracker #112): below
|
||||
// 100 % the grid is hidden but parts must still snap every 10 units.
|
||||
class tst_elementviewgrid : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private slots:
|
||||
void stepAtZoom_data()
|
||||
{
|
||||
QTest::addColumn<qreal>("zoom");
|
||||
QTest::addColumn<int>("step");
|
||||
QTest::addColumn<bool>("drawGrid");
|
||||
QTest::addColumn<bool>("drawCross");
|
||||
|
||||
QTest::newRow("10 % (fit to a big symbol)") << 0.10 << 10 << false << false;
|
||||
QTest::newRow("62.5 %, the #112 case") << 0.625 << 10 << false << false;
|
||||
QTest::newRow("100 %") << 1.0 << 10 << true << false;
|
||||
QTest::newRow("200 %") << 2.0 << 10 << true << false;
|
||||
QTest::newRow("400 %") << 4.0 << 5 << true << true;
|
||||
QTest::newRow("800 %") << 8.0 << 2 << true << true;
|
||||
QTest::newRow("1000 %") << 10.0 << 1 << true << true;
|
||||
|
||||
// Just below each threshold: the comparisons are strict (<), so a
|
||||
// change to <= would move these rows into the next band.
|
||||
QTest::newRow("99.9 %") << 0.999 << 10 << false << false;
|
||||
QTest::newRow("399.9 %") << 3.999 << 10 << true << false;
|
||||
QTest::newRow("401 %") << 4.01 << 5 << true << true;
|
||||
QTest::newRow("799.9 %") << 7.999 << 5 << true << true;
|
||||
QTest::newRow("999.9 %") << 9.999 << 2 << true << true;
|
||||
}
|
||||
|
||||
void stepAtZoom()
|
||||
{
|
||||
QFETCH(qreal, zoom);
|
||||
QFETCH(int, step);
|
||||
QFETCH(bool, drawGrid);
|
||||
QFETCH(bool, drawCross);
|
||||
|
||||
const ElementViewGrid grid = ElementViewGrid::forZoom(zoom);
|
||||
QCOMPARE(grid.step, step);
|
||||
QCOMPARE(grid.draw_grid, drawGrid);
|
||||
QCOMPARE(grid.draw_cross, drawCross);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_elementviewgrid)
|
||||
|
||||
#include "tst_elementviewgrid.moc"
|
||||
@@ -0,0 +1,95 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <QtTest>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
// Placing the same symbol twice in lmdg.qet: its embedded collection has a
|
||||
// "k_elem" category before "import". Imported symbols were filed under the
|
||||
// first category while every lookup is under "import/", so the second
|
||||
// placement could not find the first copy, tried to import it again, and
|
||||
// failed -- in a script (qet.addElement() returned "") and in the editor,
|
||||
// where the drop silently placed nothing.
|
||||
class tst_importcategory : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
|
||||
// Run @p script on @p project in a sandbox of its own and return the
|
||||
// JSON object it logged.
|
||||
QJsonObject run(const QString &script, const QString &project)
|
||||
{
|
||||
const QString path = m_dir.filePath(QStringLiteral("probe.js"));
|
||||
const QString home = m_dir.filePath(QStringLiteral("home"));
|
||||
QDir().mkpath(home + QStringLiteral("/config/QElectroTech"));
|
||||
//The shipped collection, so common:// paths resolve; forward
|
||||
//slashes, as Qt reads a backslash here as an escape.
|
||||
QFile conf(home + QStringLiteral("/config/QElectroTech/QElectroTech.conf"));
|
||||
if (!conf.open(QIODevice::WriteOnly)) return {};
|
||||
conf.write("[elements-collections]\ncommon-collection-path="
|
||||
QET_ELEMENTS_DIR "\n");
|
||||
conf.close();
|
||||
QFile f(path);
|
||||
if (!f.open(QIODevice::WriteOnly)) return {};
|
||||
f.write(script.toUtf8());
|
||||
f.close();
|
||||
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
|
||||
env.insert(QStringLiteral("HOME"), home);
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config"));
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data"));
|
||||
env.insert(QStringLiteral("TMPDIR"), m_dir.path());
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH),
|
||||
{QStringLiteral("--run"), path, project});
|
||||
if (!proc.waitForFinished(120000)) return {};
|
||||
const QString out = QString::fromUtf8(proc.readAllStandardOutput()
|
||||
+ proc.readAllStandardError());
|
||||
const QString mark = QStringLiteral("PROBE ");
|
||||
for (const QString &line : out.split(QLatin1Char('\n'))) {
|
||||
const int i = line.indexOf(mark);
|
||||
if (i >= 0)
|
||||
return QJsonDocument::fromJson(line.mid(i + mark.size()).toUtf8()).object();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY(m_dir.isValid());
|
||||
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
|
||||
}
|
||||
|
||||
void sameSymbolTwiceInLmdg()
|
||||
{
|
||||
const QString project = m_dir.filePath(QStringLiteral("lmdg.qet"));
|
||||
QVERIFY(QFile::copy(QStringLiteral(QET_EXAMPLES_DIR "/lmdg.qet"), project));
|
||||
|
||||
const QJsonObject r = run(QStringLiteral(
|
||||
"var p = 'common://10_electric/10_allpole/310_relays_contactors_contacts/"
|
||||
"01_coils/bobine_ka_a_remanence.elmt';\n"
|
||||
"var a = qet.addElement(0, p, 100, 100);\n"
|
||||
"var b = qet.addElement(0, p, 300, 100);\n"
|
||||
"qet.log('PROBE ' + JSON.stringify({first: a, second: b}));\n"),
|
||||
project);
|
||||
QVERIFY2(!r.isEmpty(), "the script logged nothing");
|
||||
QVERIFY2(!r.value(QStringLiteral("first")).toString().isEmpty(),
|
||||
"the first placement failed");
|
||||
QVERIFY2(!r.value(QStringLiteral("second")).toString().isEmpty(),
|
||||
"the second placement of the same symbol failed");
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_importcategory)
|
||||
|
||||
#include "tst_importcategory.moc"
|
||||
@@ -0,0 +1,81 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <QtTest>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QRegularExpression>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
// With "one text per potential" on, the potential's number is drawn on its
|
||||
// longest conductor. fixtures/one_text_per_potential_tie.qet has two
|
||||
// conductors of the same, greatest length in one potential: which of them
|
||||
// carried the number used to follow pointer order, so the same file
|
||||
// exported twice could put it on either. Runs the real binary, in separate
|
||||
// processes, since that order only changes from one run to the next.
|
||||
class tst_potentialtextcarrier : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
|
||||
// Export the fixture to SVG and return where the number is drawn: the
|
||||
// transform of the group holding it. Empty if it is not drawn exactly
|
||||
// once.
|
||||
QString carrier(int run)
|
||||
{
|
||||
const QString home = m_dir.filePath(QStringLiteral("home%1").arg(run));
|
||||
const QString out = m_dir.filePath(QStringLiteral("svg%1").arg(run));
|
||||
QDir().mkpath(home);
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
env.insert(QStringLiteral("HOME"), home);
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/config"));
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/data"));
|
||||
env.insert(QStringLiteral("TMPDIR"), home);
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH),
|
||||
{QStringLiteral("--export-svg"),
|
||||
QFINDTESTDATA("fixtures/one_text_per_potential_tie.qet"), out});
|
||||
if (!proc.waitForFinished(60000) || proc.exitCode() != 0)
|
||||
return {};
|
||||
|
||||
const QStringList svgs = QDir(out).entryList({QStringLiteral("*.svg")});
|
||||
if (svgs.size() != 1)
|
||||
return {};
|
||||
QFile f(QDir(out).filePath(svgs.first()));
|
||||
if (!f.open(QIODevice::ReadOnly))
|
||||
return {};
|
||||
const QString svg = QString::fromUtf8(f.readAll());
|
||||
|
||||
static const QRegularExpression re(
|
||||
QStringLiteral("transform=\"([^\"]*)\"[^<]*>\\s*<text[^>]*>42</text>"));
|
||||
QStringList found;
|
||||
auto it = re.globalMatch(svg);
|
||||
while (it.hasNext())
|
||||
found << it.next().captured(1);
|
||||
return found.size() == 1 ? found.first() : QString();
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY(m_dir.isValid());
|
||||
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
|
||||
}
|
||||
|
||||
// Before the fix about half the runs differed from the first, so
|
||||
// eight agreeing runs leave a 1 in 128 chance of a false pass.
|
||||
void sameCarrierEveryRun()
|
||||
{
|
||||
const QString first = carrier(0);
|
||||
QVERIFY2(!first.isEmpty(), "the number is not drawn exactly once");
|
||||
for (int run = 1; run < 8; ++run)
|
||||
QCOMPARE(carrier(run), first);
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_GUILESS_MAIN(tst_potentialtextcarrier)
|
||||
#include "tst_potentialtextcarrier.moc"
|
||||
@@ -0,0 +1,115 @@
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
#include <QtTest>
|
||||
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
// qet.addElement() compares the uuid of the symbol it is given with the copy
|
||||
// already embedded under the same name. A symbol file that exists but cannot
|
||||
// be read has no uuid, and was reported as "would collide with a different
|
||||
// element" (#1178 follow-up: on Windows, any symbol whose full path reaches
|
||||
// 260 characters). Made unreadable here with the file's permissions, which
|
||||
// gives the same null uuid on Linux.
|
||||
class tst_unreadableelement : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QTemporaryDir m_dir;
|
||||
QString m_collection;
|
||||
QString m_symbol;
|
||||
|
||||
// Runs a script placing common://custom/my_siren.elmt into project and
|
||||
// saving it to output; returns everything the run printed.
|
||||
QString placeTheSymbol(const QString &project, const QString &output)
|
||||
{
|
||||
const QString root = m_dir.path();
|
||||
const QString home = root + QStringLiteral("/home");
|
||||
const QString settings = root + QStringLiteral("/settings");
|
||||
QDir().mkpath(settings + QStringLiteral("/QElectroTech"));
|
||||
QDir().mkpath(root + QStringLiteral("/tmp"));
|
||||
QFile ini(settings + QStringLiteral("/QElectroTech/QElectroTech.ini"));
|
||||
if (!ini.open(QIODevice::WriteOnly | QIODevice::Text))
|
||||
return QString();
|
||||
ini.write("[elements-collections]\ncommon-collection-path=");
|
||||
ini.write(m_collection.toUtf8());
|
||||
ini.write("\n");
|
||||
ini.close();
|
||||
|
||||
const QString script = root + QStringLiteral("/add.js");
|
||||
QFile js(script);
|
||||
if (!js.open(QIODevice::WriteOnly))
|
||||
return QString();
|
||||
js.write("var u = qet.addElement(0, 'common://custom/my_siren.elmt', 100, 100);\n"
|
||||
"qet.log('RESULT ' + (u ? 'placed' : 'refused'));\n"
|
||||
"if (u) qet.save('");
|
||||
js.write(output.toUtf8());
|
||||
js.write("');\n");
|
||||
js.close();
|
||||
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("QT_QPA_PLATFORM"), QStringLiteral("offscreen"));
|
||||
env.insert(QStringLiteral("QET_ENABLE_SCRIPTING"), QStringLiteral("1"));
|
||||
env.insert(QStringLiteral("HOME"), home);
|
||||
env.insert(QStringLiteral("XDG_CONFIG_HOME"), home + QStringLiteral("/.config"));
|
||||
env.insert(QStringLiteral("XDG_DATA_HOME"), home + QStringLiteral("/.local/share"));
|
||||
env.insert(QStringLiteral("TMPDIR"), root + QStringLiteral("/tmp"));
|
||||
env.insert(QStringLiteral("QET_SETTINGS_DIR"), settings);
|
||||
|
||||
QProcess proc;
|
||||
proc.setProcessEnvironment(env);
|
||||
proc.setProcessChannelMode(QProcess::MergedChannels);
|
||||
proc.start(QStringLiteral(QET_TEST_BINARY_PATH), {QStringLiteral("--run"), script, project});
|
||||
if (!proc.waitForFinished(60000))
|
||||
return QString();
|
||||
return QString::fromUtf8(proc.readAll());
|
||||
}
|
||||
|
||||
private slots:
|
||||
void initTestCase()
|
||||
{
|
||||
QVERIFY(m_dir.isValid());
|
||||
QVERIFY(QFile::exists(QStringLiteral(QET_TEST_BINARY_PATH)));
|
||||
m_collection = m_dir.filePath(QStringLiteral("collection"));
|
||||
m_symbol = m_collection + QStringLiteral("/custom/my_siren.elmt");
|
||||
QVERIFY(QDir().mkpath(m_collection + QStringLiteral("/custom")));
|
||||
QVERIFY(QFile::copy(QStringLiteral(QET_ELEMENTS_DIR "/10_electric/10_allpole/380_signaling_operating/12_acoustic_signaling/sirene.elmt"),
|
||||
m_symbol));
|
||||
QVERIFY(QFile::copy(QFINDTESTDATA("fixtures/qet_bug_repro_resaved.qet"),
|
||||
m_dir.filePath(QStringLiteral("p.qet"))));
|
||||
}
|
||||
|
||||
void anUnreadableSymbolIsNotACollision()
|
||||
{
|
||||
// Placed once while readable: the project now embeds it.
|
||||
const QString embedded = m_dir.filePath(QStringLiteral("embedded.qet"));
|
||||
QVERIFY(placeTheSymbol(m_dir.filePath(QStringLiteral("p.qet")), embedded)
|
||||
.contains(QStringLiteral("RESULT placed")));
|
||||
QVERIFY(QFile::exists(embedded));
|
||||
|
||||
QVERIFY(QFile::setPermissions(m_symbol, QFileDevice::Permissions()));
|
||||
QFile probe(m_symbol);
|
||||
if (probe.open(QIODevice::ReadOnly))
|
||||
QSKIP("permissions do not stop this user reading the file (root?)");
|
||||
|
||||
const QString out = placeTheSymbol(embedded, m_dir.filePath(QStringLiteral("again.qet")));
|
||||
QFile::setPermissions(m_symbol, QFileDevice::ReadOwner | QFileDevice::WriteOwner);
|
||||
QVERIFY2(out.contains(QStringLiteral("RESULT refused")), qPrintable(out));
|
||||
QVERIFY2(out.contains(QStringLiteral("could not read element")), qPrintable(out));
|
||||
QVERIFY2(!out.contains(QStringLiteral("would collide")), qPrintable(out));
|
||||
}
|
||||
|
||||
// Readable again, the same symbol is placed a second time as before.
|
||||
void theSameSymbolIsPlacedAgain()
|
||||
{
|
||||
const QString out = placeTheSymbol(m_dir.filePath(QStringLiteral("embedded.qet")),
|
||||
m_dir.filePath(QStringLiteral("twice.qet")));
|
||||
QVERIFY2(out.contains(QStringLiteral("RESULT placed")), qPrintable(out));
|
||||
}
|
||||
};
|
||||
|
||||
QTEST_APPLESS_MAIN(tst_unreadableelement)
|
||||
|
||||
#include "tst_unreadableelement.moc"
|
||||
Reference in New Issue
Block a user