2021-01-08 10:00:12 +03:00
import logging
2020-01-26 21:32:04 +03:00
import pytest
2018-09-21 20:46:40 +03:00
from gaphor import UML
2020-01-26 21:32:04 +03:00
from gaphor . application import Application
2023-05-11 12:18:53 +03:00
from gaphor . core import Transaction , event_handler
2023-11-24 22:20:06 +03:00
from gaphor . core . modeling import AssociationUpdated , Diagram
2024-02-03 14:22:16 +03:00
from gaphor . diagram . copypaste import copy_full , paste_full
2020-12-22 12:50:48 +03:00
from gaphor . diagram . tests . fixtures import connect
2023-05-10 23:03:15 +03:00
from gaphor . UML . classes import (
AssociationItem ,
ClassItem ,
2024-02-03 14:22:16 +03:00
ContainmentItem ,
2023-05-10 23:03:15 +03:00
GeneralizationItem ,
)
2022-03-27 22:23:11 +03:00
from gaphor . UML . interactions import MessageItem
from gaphor . UML . interactions . interactionstoolbox import reflexive_message_config
2020-01-26 21:32:04 +03:00
@pytest.fixture
def application ( ) :
app = Application ( )
yield app
app . shutdown ( )
@pytest.fixture
def session ( application ) :
return application . new_session ( )
2020-08-18 17:59:20 +03:00
@pytest.fixture
def event_manager ( session ) :
return session . get_service ( " event_manager " )
2020-01-26 21:32:04 +03:00
@pytest.fixture
def element_factory ( session ) :
return session . get_service ( " element_factory " )
@pytest.fixture
def undo_manager ( session ) :
return session . get_service ( " undo_manager " )
2020-08-18 17:59:20 +03:00
def test_class_association_undo_redo ( event_manager , element_factory , undo_manager ) :
2021-04-04 13:01:58 +03:00
diagram , ci1 , ci2 , a = set_up_class_and_association ( event_manager , element_factory )
2010-02-10 10:25:22 +03:00
2022-09-10 12:49:08 +03:00
def get_connected ( handle ) :
""" Get item connected to line via handle. """
if cinfo := diagram . connections . get_connection ( handle ) :
return cinfo . connected
return None
2020-01-26 21:32:04 +03:00
undo_manager . clear_undo_stack ( )
assert not undo_manager . can_undo ( )
2010-02-10 10:25:22 +03:00
2020-08-18 17:59:20 +03:00
with Transaction ( event_manager ) :
ci2 . unlink ( )
2010-02-10 10:25:22 +03:00
2020-01-26 21:32:04 +03:00
assert undo_manager . can_undo ( )
2010-02-10 10:25:22 +03:00
2023-01-30 04:40:39 +03:00
for _i in range ( 3 ) :
2024-02-03 15:03:59 +03:00
assert 9 == len ( diagram . connections . solver . constraints )
2010-02-11 08:42:27 +03:00
2020-01-26 21:32:04 +03:00
undo_manager . undo_transaction ( )
2010-02-11 08:42:27 +03:00
2021-02-07 16:26:10 +03:00
assert 18 == len ( diagram . connections . solver . constraints )
2010-02-11 08:42:27 +03:00
2022-09-10 12:49:08 +03:00
a = next ( element_factory . select ( AssociationItem ) )
2020-01-26 21:32:04 +03:00
assert ci1 == get_connected ( a . head )
2021-01-06 17:20:15 +03:00
assert ci2 . id == get_connected ( a . tail ) . id
2010-02-11 08:42:27 +03:00
2020-01-26 21:32:04 +03:00
undo_manager . redo_transaction ( )
2020-08-18 17:59:20 +03:00
2021-04-04 13:01:58 +03:00
def set_up_class_and_association ( event_manager , element_factory ) :
with Transaction ( event_manager ) :
diagram = element_factory . create ( Diagram )
assert 0 == len ( diagram . connections . solver . constraints )
with Transaction ( event_manager ) :
ci1 = diagram . create ( ClassItem , subject = element_factory . create ( UML . Class ) )
assert 8 == len ( diagram . connections . solver . constraints )
with Transaction ( event_manager ) :
ci2 = diagram . create ( ClassItem , subject = element_factory . create ( UML . Class ) )
assert 16 == len ( diagram . connections . solver . constraints )
with Transaction ( event_manager ) :
a = diagram . create ( AssociationItem )
connect ( a , a . head , ci1 )
connect ( a , a . tail , ci2 )
# Diagram, Association, 2x Class, Property, LiteralSpecification
2021-05-29 22:36:21 +03:00
assert 12 == len ( element_factory . lselect ( ) )
2021-04-04 13:01:58 +03:00
assert 18 == len ( diagram . connections . solver . constraints )
return diagram , ci1 , ci2 , a
2021-02-11 23:46:40 +03:00
def test_diagram_item_can_undo_and_redo (
event_manager , element_factory , undo_manager , caplog
) :
2021-01-08 10:00:12 +03:00
caplog . set_level ( logging . INFO )
with Transaction ( event_manager ) :
2021-01-09 02:23:37 +03:00
diagram = element_factory . create ( Diagram )
2021-01-08 10:00:12 +03:00
with Transaction ( event_manager ) :
cls = diagram . create ( ClassItem , subject = element_factory . create ( UML . Class ) )
2021-02-11 23:46:40 +03:00
cls . subject . name = " name "
2021-01-08 10:00:12 +03:00
cls . matrix . translate ( 10 , 10 )
undo_manager . undo_transaction ( )
undo_manager . redo_transaction ( )
2021-02-11 23:46:40 +03:00
new_cls = diagram . ownedPresentation [ 0 ]
assert new_cls . matrix . tuple ( ) == ( 1 , 0 , 0 , 1 , 10 , 10 )
assert new_cls . subject , element_factory . select ( )
assert new_cls . subject . name == " name "
2021-01-08 10:00:12 +03:00
assert not caplog . records
2020-08-18 17:59:20 +03:00
def test_diagram_item_should_not_end_up_in_element_factory (
event_manager , element_factory , undo_manager
) :
2020-12-28 23:57:56 +03:00
with Transaction ( event_manager ) :
2021-01-09 02:23:37 +03:00
diagram = element_factory . create ( Diagram )
2020-08-18 17:59:20 +03:00
with Transaction ( event_manager ) :
cls = diagram . create ( ClassItem , subject = element_factory . create ( UML . Class ) )
undo_manager . undo_transaction ( )
undo_manager . redo_transaction ( )
assert cls not in element_factory . lselect ( ) , element_factory . lselect ( )
2021-02-05 21:58:56 +03:00
def test_delete_and_undo_diagram_item ( event_manager , element_factory , undo_manager ) :
with Transaction ( event_manager ) :
diagram = element_factory . create ( Diagram )
with Transaction ( event_manager ) :
subject = element_factory . create ( UML . Class )
subject . name = " Name "
cls = diagram . create ( ClassItem , subject = subject )
with Transaction ( event_manager ) :
cls . unlink ( )
undo_manager . undo_transaction ( )
new_cls = diagram . ownedPresentation [ 0 ]
new_elem = element_factory . lookup ( subject . id )
assert new_cls in new_elem . presentation
assert new_cls . subject
assert new_elem . name == " Name "
def test_delete_and_undo_model_element ( event_manager , element_factory , undo_manager ) :
with Transaction ( event_manager ) :
diagram = element_factory . create ( Diagram )
with Transaction ( event_manager ) :
subject = element_factory . create ( UML . Class )
subject . name = " Name "
diagram . create ( ClassItem , subject = subject )
with Transaction ( event_manager ) :
subject . unlink ( )
undo_manager . undo_transaction ( )
new_cls = diagram . ownedPresentation [ 0 ]
new_elem = element_factory . lookup ( subject . id )
assert new_cls in new_elem . presentation
assert new_cls . subject
assert new_elem . name == " Name "
2020-08-18 17:59:20 +03:00
def test_deleted_diagram_item_should_not_end_up_in_element_factory (
event_manager , element_factory , undo_manager
) :
2020-12-28 23:57:56 +03:00
with Transaction ( event_manager ) :
2021-01-09 02:23:37 +03:00
diagram = element_factory . create ( Diagram )
2020-12-28 23:57:56 +03:00
cls = diagram . create ( ClassItem , subject = element_factory . create ( UML . Class ) )
2020-08-18 17:59:20 +03:00
with Transaction ( event_manager ) :
cls . unlink ( )
undo_manager . undo_transaction ( )
assert cls not in element_factory . lselect ( ) , element_factory . lselect ( )
undo_manager . redo_transaction ( )
assert cls not in element_factory . lselect ( ) , element_factory . lselect ( )
2021-01-01 13:49:30 +03:00
def test_undo_should_not_cause_warnings (
2021-01-08 10:00:12 +03:00
event_manager , element_factory , undo_manager , caplog
2021-01-01 13:49:30 +03:00
) :
2021-01-08 10:00:12 +03:00
caplog . set_level ( logging . INFO )
2021-01-01 13:49:30 +03:00
with Transaction ( event_manager ) :
2021-01-09 02:23:37 +03:00
diagram = element_factory . create ( Diagram )
2021-01-01 13:49:30 +03:00
with Transaction ( event_manager ) :
diagram . create ( ClassItem , subject = element_factory . create ( UML . Class ) )
2021-01-08 10:00:12 +03:00
assert not caplog . records
2021-01-01 13:49:30 +03:00
undo_manager . undo_transaction ( )
2021-01-08 10:00:12 +03:00
assert not diagram . ownedPresentation
assert not caplog . records
2021-01-09 02:23:37 +03:00
def test_can_undo_connected_generalization (
event_manager , element_factory , undo_manager , caplog
) :
caplog . set_level ( logging . INFO )
with Transaction ( event_manager ) :
diagram : Diagram = element_factory . create ( Diagram )
general = diagram . create ( ClassItem , subject = element_factory . create ( UML . Class ) )
specific = diagram . create ( ClassItem , subject = element_factory . create ( UML . Class ) )
with Transaction ( event_manager ) :
generalization = diagram . create ( GeneralizationItem )
connect ( generalization , generalization . head , general )
connect ( generalization , generalization . tail , specific )
assert not caplog . records
undo_manager . undo_transaction ( )
assert not list ( diagram . select ( GeneralizationItem ) )
assert not caplog . records
undo_manager . redo_transaction ( )
new_generalization_item = next ( diagram . select ( GeneralizationItem ) )
new_generalization = next ( element_factory . select ( UML . Generalization ) )
assert len ( list ( diagram . select ( GeneralizationItem ) ) ) == 1
assert len ( element_factory . lselect ( UML . Generalization ) ) == 1
assert new_generalization_item . subject is new_generalization
assert not caplog . records
def test_can_undo_connected_association (
event_manager , element_factory , undo_manager , caplog
) :
caplog . set_level ( logging . INFO )
with Transaction ( event_manager ) :
diagram : Diagram = element_factory . create ( Diagram )
parent = diagram . create ( ClassItem , subject = element_factory . create ( UML . Class ) )
child = diagram . create ( ClassItem , subject = element_factory . create ( UML . Class ) )
with Transaction ( event_manager ) :
association = diagram . create ( AssociationItem )
connect ( association , association . head , parent )
connect ( association , association . tail , child )
assert not caplog . records
undo_manager . undo_transaction ( )
assert not list ( diagram . select ( AssociationItem ) )
assert not caplog . records
undo_manager . redo_transaction ( )
new_association_item = next ( diagram . select ( AssociationItem ) )
new_association = next ( element_factory . select ( UML . Association ) )
assert len ( list ( diagram . select ( AssociationItem ) ) ) == 1
assert len ( element_factory . lselect ( UML . Association ) ) == 1
assert len ( new_association . memberEnd ) == 2
assert new_association_item . subject is new_association
assert new_association_item . head_subject
assert new_association_item . tail_subject
assert not caplog . records
2021-05-24 23:19:44 +03:00
def test_can_undo_diagram_with_content ( event_manager , element_factory , undo_manager ) :
with Transaction ( event_manager ) :
diagram : Diagram = element_factory . create ( Diagram )
diagram . create ( ClassItem , subject = element_factory . create ( UML . Class ) )
with Transaction ( event_manager ) :
diagram . unlink ( )
undo_manager . undo_transaction ( )
2021-05-25 10:33:59 +03:00
new_diagram = element_factory . lookup ( diagram . id )
2021-05-24 23:19:44 +03:00
assert new_diagram
assert new_diagram . ownedPresentation
2022-03-27 22:23:11 +03:00
def test_reflexive_message_undo ( event_manager , element_factory , undo_manager ) :
with Transaction ( event_manager ) :
diagram : Diagram = element_factory . create ( Diagram )
with Transaction ( event_manager ) :
message = diagram . create ( MessageItem )
reflexive_message_config ( message )
undo_manager . undo_transaction ( )
2022-04-12 18:04:10 +03:00
def test_delete_item_with_subject_owning_diagram (
event_manager , element_factory , undo_manager
) :
with Transaction ( event_manager ) :
diagram : Diagram = element_factory . create ( Diagram )
klass = element_factory . create ( UML . Class )
class_item = diagram . create ( ClassItem , subject = klass )
diagram . element = klass
with Transaction ( event_manager ) :
class_item . unlink ( )
undo_manager . undo_transaction ( )
2022-10-03 13:05:41 +03:00
def test_reconnect_on_same_element ( event_manager , element_factory , undo_manager ) :
2024-03-24 14:04:13 +03:00
def copy_pos ( pos ) :
return tuple ( map ( float , pos ) )
2022-10-03 13:05:41 +03:00
with Transaction ( event_manager ) :
diagram : Diagram = element_factory . create ( Diagram )
klass = element_factory . create ( UML . Class )
class_item = diagram . create ( ClassItem , subject = klass )
association = diagram . create ( AssociationItem )
connect ( association , association . head , class_item )
original_handle_pos = copy_pos ( association . head . pos )
with Transaction ( event_manager ) :
association . head . pos = ( class_item . width , class_item . height )
connect ( association , association . head , class_item )
new_handle_pos = copy_pos ( association . head . pos )
assert original_handle_pos != new_handle_pos
undo_manager . undo_transaction ( )
2024-03-24 14:44:07 +03:00
diagram . update ( diagram . ownedPresentation )
2022-10-03 13:05:41 +03:00
assert original_handle_pos == copy_pos ( association . head . pos )
2023-05-10 23:03:15 +03:00
2023-05-11 12:18:53 +03:00
def test_exception_raised_during_undo ( event_manager , element_factory , undo_manager ) :
package : UML . Package = next ( element_factory . select ( UML . Package ) )
2023-05-10 23:03:15 +03:00
2023-05-11 12:18:53 +03:00
with Transaction ( event_manager ) :
2023-05-10 23:03:15 +03:00
klass = element_factory . create ( UML . Class )
klass . package = package
2023-05-11 12:18:53 +03:00
with Transaction ( event_manager ) :
klass . unlink ( )
2023-05-10 23:03:15 +03:00
2023-05-11 12:18:53 +03:00
@event_handler ( AssociationUpdated )
def raise_an_exception ( event ) :
if event . property is UML . Class . package :
raise ValueError ( " Test exception " )
2023-05-10 23:03:15 +03:00
2023-05-11 12:18:53 +03:00
event_manager . subscribe ( raise_an_exception )
2023-05-10 23:03:15 +03:00
2023-05-11 12:18:53 +03:00
with pytest . raises ( ExceptionGroup ) :
undo_manager . undo_transaction ( )
2023-05-10 23:03:15 +03:00
2023-05-11 12:34:30 +03:00
def test_exception_raised_during_undo_from_event_handler (
event_manager , element_factory , undo_manager
) :
2024-02-03 14:22:16 +03:00
class UndoRequested :
pass
2023-05-11 12:34:30 +03:00
package : UML . Package = next ( element_factory . select ( UML . Package ) )
with Transaction ( event_manager ) :
klass = element_factory . create ( UML . Class )
klass . package = package
with Transaction ( event_manager ) :
klass . unlink ( )
@event_handler ( AssociationUpdated )
def raise_an_exception ( event ) :
if event . property is UML . Class . package :
raise ValueError ( " Test exception " )
event_manager . subscribe ( raise_an_exception )
@event_handler ( UndoRequested )
def on_undo_requested ( event ) :
undo_manager . undo_transaction ( )
event_manager . subscribe ( on_undo_requested )
with pytest . raises ( ExceptionGroup ) :
event_manager . handle ( UndoRequested ( ) )
# Handle remaining events on the queue
event_manager . handle ( )
2024-02-03 14:22:16 +03:00
def test_undo_paste_full ( create , diagram , event_manager , undo_manager ) :
""" Triggers an error when pasted data is undone.
Reproduce Hypothesis test found in https : / / github . com / gaphor / gaphor / issues / 2895.
"""
with Transaction ( event_manager ) :
class_item = create ( ClassItem , UML . Class )
containment_item = create ( ContainmentItem )
connect ( containment_item , containment_item . head , class_item )
copy_buffer = copy_full ( [ containment_item , class_item ] )
with Transaction ( event_manager ) :
paste_full ( copy_buffer , diagram )
undo_manager . undo_transaction ( )