Convert tests from unittest to pytest
Signed-off-by: Dan Yeaw <dan@yeaw.me>
This commit is contained in:
parent
82f64b8362
commit
9d9c48375e
@ -6,6 +6,9 @@ from __future__ import print_function
|
||||
import threading
|
||||
from builtins import object
|
||||
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk, GLib
|
||||
|
||||
DEBUG_ASYNC = False
|
||||
|
94
gaphas/tests/conftest.py
Normal file
94
gaphas/tests/conftest.py
Normal file
@ -0,0 +1,94 @@
|
||||
import pytest
|
||||
import gi
|
||||
|
||||
gi.require_version("Gtk", "3.0")
|
||||
from gi.repository import Gtk
|
||||
|
||||
from gaphas import state
|
||||
from gaphas.canvas import Canvas
|
||||
from gaphas.examples import Box
|
||||
from gaphas.item import Line
|
||||
from gaphas.tool import ConnectHandleTool
|
||||
from gaphas.view import GtkView
|
||||
|
||||
|
||||
class SimpleCanvas(object):
|
||||
"""Creates a test canvas object.
|
||||
|
||||
Adds a view, canvas, and handle connection tool to a test
|
||||
case. Two boxes and a line are added to the canvas as well.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.canvas = Canvas()
|
||||
|
||||
self.box1 = Box()
|
||||
self.canvas.add(self.box1)
|
||||
self.box1.matrix.translate(100, 50)
|
||||
self.box1.width = 40
|
||||
self.box1.height = 40
|
||||
self.box1.request_update()
|
||||
|
||||
self.box2 = Box()
|
||||
self.canvas.add(self.box2)
|
||||
self.box2.matrix.translate(100, 150)
|
||||
self.box2.width = 50
|
||||
self.box2.height = 50
|
||||
self.box2.request_update()
|
||||
|
||||
self.line = Line()
|
||||
self.head = self.line.handles()[0]
|
||||
self.tail = self.line.handles()[-1]
|
||||
self.tail.pos = 100, 100
|
||||
self.canvas.add(self.line)
|
||||
|
||||
self.canvas.update_now()
|
||||
self.view = GtkView()
|
||||
self.view.canvas = self.canvas
|
||||
|
||||
self.win = Gtk.Window()
|
||||
self.win.add(self.view)
|
||||
self.view.show()
|
||||
self.view.update()
|
||||
self.win.show()
|
||||
|
||||
self.tool = ConnectHandleTool(self.view)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def simple_canvas():
|
||||
"""Creates a `SimpleCanvas`.
|
||||
|
||||
"""
|
||||
return SimpleCanvas()
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
def undo_fixture():
|
||||
undo_list = []
|
||||
redo_list = []
|
||||
|
||||
def undo():
|
||||
apply_me = list(undo_list)
|
||||
del undo_list[:]
|
||||
apply_me.reverse()
|
||||
for e in apply_me:
|
||||
state.saveapply(*e)
|
||||
redo_list[:] = undo_list[:]
|
||||
del undo_list[:]
|
||||
|
||||
def undo_handler(event):
|
||||
undo_list.append(event)
|
||||
|
||||
return undo, undo_handler, undo_list
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def revert_undo(undo_fixture):
|
||||
state.observers.clear()
|
||||
state.subscribers.clear()
|
||||
state.observers.add(state.revert_handler)
|
||||
state.subscribers.add(undo_fixture[1])
|
||||
yield
|
||||
state.observers.remove(state.revert_handler)
|
||||
state.subscribers.remove(undo_fixture[1])
|
@ -1,51 +1,48 @@
|
||||
"""
|
||||
Generic gaphas item tests.
|
||||
"""Test aspects for items.
|
||||
|
||||
"""
|
||||
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
from gaphas.aspect import *
|
||||
from gaphas.canvas import Canvas
|
||||
from gaphas.view import View
|
||||
|
||||
|
||||
class AspectTestCase(unittest.TestCase):
|
||||
"""
|
||||
Test aspects for items.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
class CanvasViewItem(object):
|
||||
def __init__(self):
|
||||
self.canvas = Canvas()
|
||||
self.view = View(self.canvas)
|
||||
|
||||
def test_selection_select(self):
|
||||
"""
|
||||
Test the Selection role methods
|
||||
"""
|
||||
view = self.view
|
||||
item = Item()
|
||||
self.canvas.add(item)
|
||||
selection = Selection(item, view)
|
||||
assert item not in view.selected_items
|
||||
selection.select()
|
||||
assert item in view.selected_items
|
||||
assert item is view.focused_item
|
||||
selection.unselect()
|
||||
assert item not in view.selected_items
|
||||
assert None is view.focused_item
|
||||
|
||||
def test_selection_move(self):
|
||||
"""
|
||||
Test the Selection role methods
|
||||
"""
|
||||
view = self.view
|
||||
item = Item()
|
||||
self.canvas.add(item)
|
||||
inmotion = InMotion(item, view)
|
||||
self.assertEqual((1, 0, 0, 1, 0, 0), tuple(item.matrix))
|
||||
inmotion.start_move((0, 0))
|
||||
inmotion.move((12, 26))
|
||||
self.assertEqual((1, 0, 0, 1, 12, 26), tuple(item.matrix))
|
||||
self.item = Item()
|
||||
|
||||
|
||||
# vim:sw=4:et:ai
|
||||
@pytest.fixture()
|
||||
def cvi():
|
||||
return CanvasViewItem()
|
||||
|
||||
|
||||
def test_selection_select(cvi):
|
||||
"""Test the Selection role methods.
|
||||
|
||||
"""
|
||||
cvi.canvas.add(cvi.item)
|
||||
selection = Selection(cvi.item, cvi.view)
|
||||
assert cvi.item not in cvi.view.selected_items
|
||||
selection.select()
|
||||
assert cvi.item in cvi.view.selected_items
|
||||
assert cvi.item is cvi.view.focused_item
|
||||
selection.unselect()
|
||||
assert cvi.item not in cvi.view.selected_items
|
||||
assert None is cvi.view.focused_item
|
||||
|
||||
|
||||
def test_selection_move(cvi):
|
||||
"""Test the Selection role methods.
|
||||
|
||||
"""
|
||||
cvi.canvas.add(cvi.item)
|
||||
in_motion = InMotion(cvi.item, cvi.view)
|
||||
assert (1, 0, 0, 1, 0, 0) == tuple(cvi.item.matrix)
|
||||
in_motion.start_move((0, 0))
|
||||
in_motion.move((12, 26))
|
||||
assert (1, 0, 0, 1, 12, 26) == tuple(cvi.item.matrix)
|
||||
|
@ -1,36 +1,35 @@
|
||||
import unittest
|
||||
|
||||
import cairo
|
||||
import pytest
|
||||
|
||||
from gaphas.canvas import Canvas, ConnectionError
|
||||
from gaphas.examples import Box
|
||||
from gaphas.item import Line
|
||||
|
||||
|
||||
class MatricesTestCase(unittest.TestCase):
|
||||
def test_update_matrices(self):
|
||||
"""Test updating of matrices"""
|
||||
c = Canvas()
|
||||
i = Box()
|
||||
ii = Box()
|
||||
c.add(i)
|
||||
c.add(ii, i)
|
||||
def test_update_matrices():
|
||||
"""Test updating of matrices"""
|
||||
c = Canvas()
|
||||
i = Box()
|
||||
ii = Box()
|
||||
c.add(i)
|
||||
c.add(ii, i)
|
||||
|
||||
i.matrix = (1.0, 0.0, 0.0, 1.0, 5.0, 0.0)
|
||||
ii.matrix = (1.0, 0.0, 0.0, 1.0, 0.0, 8.0)
|
||||
i.matrix = (1.0, 0.0, 0.0, 1.0, 5.0, 0.0)
|
||||
ii.matrix = (1.0, 0.0, 0.0, 1.0, 0.0, 8.0)
|
||||
|
||||
updated = c.update_matrices([i])
|
||||
updated = c.update_matrices([i])
|
||||
|
||||
self.assertEqual(i._matrix_i2c, cairo.Matrix(1, 0, 0, 1, 5, 0))
|
||||
self.assertEqual(ii._matrix_i2c, cairo.Matrix(1, 0, 0, 1, 5, 8))
|
||||
assert i._matrix_i2c == cairo.Matrix(1, 0, 0, 1, 5, 0)
|
||||
assert ii._matrix_i2c == cairo.Matrix(1, 0, 0, 1, 5, 8)
|
||||
|
||||
def test_reparent(self):
|
||||
c = Canvas()
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
c.add(b1)
|
||||
c.add(b2, b1)
|
||||
c.reparent(b2, None)
|
||||
|
||||
def test_reparent():
|
||||
c = Canvas()
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
c.add(b1)
|
||||
c.add(b2, b1)
|
||||
c.reparent(b2, None)
|
||||
|
||||
|
||||
# fixme: what about multiple constraints for a handle?
|
||||
@ -41,316 +40,177 @@ def count(i):
|
||||
return len(list(i))
|
||||
|
||||
|
||||
class CanvasTestCase(unittest.TestCase):
|
||||
def test_connect_item(self):
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
line = Line()
|
||||
c = Canvas()
|
||||
c.add(b1)
|
||||
c.add(b2)
|
||||
c.add(line)
|
||||
def test_connect_item():
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
line = Line()
|
||||
c = Canvas()
|
||||
c.add(b1)
|
||||
c.add(b2)
|
||||
c.add(line)
|
||||
|
||||
c.connect_item(line, line.handles()[0], b1, b1.ports()[0])
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 1
|
||||
|
||||
# Add the same
|
||||
with pytest.raises(ConnectionError):
|
||||
c.connect_item(line, line.handles()[0], b1, b1.ports()[0])
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 1
|
||||
|
||||
# Add the same
|
||||
self.assertRaises(
|
||||
ConnectionError, c.connect_item, line, line.handles()[0], b1, b1.ports()[0]
|
||||
)
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 1
|
||||
|
||||
# Same item, different port
|
||||
# c.connect_item(l, l.handles()[0], b1, b1.ports()[-1])
|
||||
# assert count(c.get_connections(handle=l.handles()[0])) == 1
|
||||
|
||||
# Different item
|
||||
# c.connect_item(l, l.handles()[0], b2, b2.ports()[0])
|
||||
# assert count(c.get_connections(handle=l.handles()[0])) == 1
|
||||
|
||||
def test_disconnect_item_with_callback(self):
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
line = Line()
|
||||
c = Canvas()
|
||||
c.add(b1)
|
||||
c.add(b2)
|
||||
c.add(line)
|
||||
|
||||
events = []
|
||||
|
||||
def callback():
|
||||
events.append("called")
|
||||
|
||||
c.connect_item(line, line.handles()[0], b1, b1.ports()[0], callback=callback)
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 1
|
||||
|
||||
c.disconnect_item(line, line.handles()[0])
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 0
|
||||
assert events == ["called"]
|
||||
|
||||
def test_disconnect_item_with_constraint(self):
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
line = Line()
|
||||
c = Canvas()
|
||||
c.add(b1)
|
||||
c.add(b2)
|
||||
c.add(line)
|
||||
|
||||
cons = b1.ports()[0].constraint(c, line, line.handles()[0], b1)
|
||||
|
||||
c.connect_item(line, line.handles()[0], b1, b1.ports()[0], constraint=cons)
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 1
|
||||
|
||||
ncons = len(c.solver.constraints)
|
||||
assert ncons == 5
|
||||
|
||||
c.disconnect_item(line, line.handles()[0])
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 0
|
||||
|
||||
assert len(c.solver.constraints) == 4
|
||||
|
||||
def test_disconnect_item_by_deleting_element(self):
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
line = Line()
|
||||
c = Canvas()
|
||||
c.add(b1)
|
||||
c.add(b2)
|
||||
c.add(line)
|
||||
|
||||
events = []
|
||||
|
||||
def callback():
|
||||
events.append("called")
|
||||
|
||||
c.connect_item(line, line.handles()[0], b1, b1.ports()[0], callback=callback)
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 1
|
||||
|
||||
c.remove(b1)
|
||||
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 0
|
||||
assert events == ["called"]
|
||||
|
||||
def test_disconnect_item_with_constraint_by_deleting_element(self):
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
line = Line()
|
||||
c = Canvas()
|
||||
c.add(b1)
|
||||
c.add(b2)
|
||||
c.add(line)
|
||||
|
||||
cons = b1.ports()[0].constraint(c, line, line.handles()[0], b1)
|
||||
|
||||
c.connect_item(line, line.handles()[0], b1, b1.ports()[0], constraint=cons)
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 1
|
||||
|
||||
ncons = len(c.solver.constraints)
|
||||
assert ncons == 5
|
||||
|
||||
c.remove(b1)
|
||||
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 0
|
||||
|
||||
self.assertEqual(2, len(c.solver.constraints))
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 1
|
||||
|
||||
|
||||
class ConstraintProjectionTestCase(unittest.TestCase):
|
||||
def test_line_projection(self):
|
||||
"""Test projection with line's handle on element's side"""
|
||||
line = Line()
|
||||
line.matrix.translate(15, 50)
|
||||
h1, h2 = line.handles()
|
||||
h1.x, h1.y = 0, 0
|
||||
h2.x, h2.y = 20, 20
|
||||
def test_disconnect_item_with_callback():
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
line = Line()
|
||||
c = Canvas()
|
||||
c.add(b1)
|
||||
c.add(b2)
|
||||
c.add(line)
|
||||
|
||||
box = Box()
|
||||
box.matrix.translate(10, 10)
|
||||
box.width = 40
|
||||
box.height = 20
|
||||
h_nw, h_ne, h_se, h_sw = box.handles()
|
||||
events = []
|
||||
|
||||
canvas = Canvas()
|
||||
canvas.add(line)
|
||||
canvas.add(box)
|
||||
def callback():
|
||||
events.append("called")
|
||||
|
||||
# move line's second handle on box side
|
||||
h2.x, h2.y = 5, -20
|
||||
c.connect_item(line, line.handles()[0], b1, b1.ports()[0], callback=callback)
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 1
|
||||
|
||||
c.disconnect_item(line, line.handles()[0])
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 0
|
||||
assert events == ["called"]
|
||||
|
||||
|
||||
# bc = BalanceConstraint(band=(h_sw.x, h_se.x), v=h2.x, balance=0.25)
|
||||
# canvas.projector(bc, x={h_sw.x: box, h_se.x: box, h2.x: line})
|
||||
# canvas._solver.add_constraint(bc)
|
||||
#
|
||||
# eq = EqualsConstraint(a=h_se.y, b=h2.y)
|
||||
# canvas.projector(eq, y={h_se.y: box, h2.y: line})
|
||||
# canvas._solver.add_constraint(eq)
|
||||
#
|
||||
# box.request_update()
|
||||
# line.request_update()
|
||||
#
|
||||
# canvas.update()
|
||||
#
|
||||
# box.width = 60
|
||||
# box.height = 30
|
||||
#
|
||||
# canvas.update()
|
||||
#
|
||||
# # expect h2.x to be moved due to balance constraint
|
||||
# self.assertEqual(10, h2.x)
|
||||
# self.assertEqual(-10, h2.y)
|
||||
def test_disconnect_item_with_constraint():
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
line = Line()
|
||||
c = Canvas()
|
||||
c.add(b1)
|
||||
c.add(b2)
|
||||
c.add(line)
|
||||
|
||||
cons = b1.ports()[0].constraint(c, line, line.handles()[0], b1)
|
||||
|
||||
c.connect_item(line, line.handles()[0], b1, b1.ports()[0], constraint=cons)
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 1
|
||||
|
||||
ncons = len(c.solver.constraints)
|
||||
assert ncons == 5
|
||||
|
||||
c.disconnect_item(line, line.handles()[0])
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 0
|
||||
|
||||
assert len(c.solver.constraints) == 4
|
||||
|
||||
|
||||
class CanvasConstraintTestCase(unittest.TestCase):
|
||||
def test_remove_connected_item(self):
|
||||
"""Test adding canvas constraint"""
|
||||
canvas = Canvas()
|
||||
def test_disconnect_item_by_deleting_element():
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
line = Line()
|
||||
c = Canvas()
|
||||
c.add(b1)
|
||||
c.add(b2)
|
||||
c.add(line)
|
||||
|
||||
from gaphas.aspect import Connector, ConnectionSink
|
||||
events = []
|
||||
|
||||
l1 = Line()
|
||||
canvas.add(l1)
|
||||
def callback():
|
||||
events.append("called")
|
||||
|
||||
b1 = Box()
|
||||
canvas.add(b1)
|
||||
c.connect_item(line, line.handles()[0], b1, b1.ports()[0], callback=callback)
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 1
|
||||
|
||||
number_cons1 = len(canvas.solver.constraints)
|
||||
c.remove(b1)
|
||||
|
||||
b2 = Box()
|
||||
canvas.add(b2)
|
||||
|
||||
number_cons2 = len(canvas.solver.constraints)
|
||||
|
||||
conn = Connector(l1, l1.handles()[0])
|
||||
sink = ConnectionSink(b1, b1.ports()[0])
|
||||
|
||||
conn.connect(sink)
|
||||
|
||||
assert canvas.get_connection(l1.handles()[0])
|
||||
|
||||
conn = Connector(l1, l1.handles()[1])
|
||||
sink = ConnectionSink(b2, b2.ports()[0])
|
||||
|
||||
conn.connect(sink)
|
||||
|
||||
assert canvas.get_connection(l1.handles()[1])
|
||||
|
||||
self.assertEqual(number_cons2 + 2, len(canvas.solver.constraints))
|
||||
|
||||
canvas.remove(b1)
|
||||
|
||||
# Expecting a class + line connected at one end only
|
||||
self.assertEqual(number_cons1 + 1, len(canvas.solver.constraints))
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 0
|
||||
assert events == ["called"]
|
||||
|
||||
|
||||
# def test_adding_constraint(self):
|
||||
# """Test adding canvas constraint"""
|
||||
# canvas = Canvas()
|
||||
#
|
||||
# l1 = Line()
|
||||
# canvas.add(l1)
|
||||
#
|
||||
# h1, h2 = l1.handles()
|
||||
# h = Handle()
|
||||
#
|
||||
# eq1 = EqualsConstraint(h1.x, h.x)
|
||||
# canvas.add_canvas_constraint(l1, h1, eq1)
|
||||
# self.assertTrue(l1 in cons)
|
||||
# self.assertTrue(h1 in cons[l1])
|
||||
# self.assertTrue(eq1 in cons[l1][h1])
|
||||
#
|
||||
# l2 = Line()
|
||||
# canvas.add(l2)
|
||||
#
|
||||
# h1, h2 = l2.handles()
|
||||
# h = Handle()
|
||||
#
|
||||
# eq2 = EqualsConstraint(h1.x, h.x)
|
||||
# canvas.add_canvas_constraint(l2, h1, eq2)
|
||||
# self.assertTrue(l2 in cons)
|
||||
# self.assertTrue(h1 in cons[l2])
|
||||
# self.assertTrue(eq2 in cons[l2][h1])
|
||||
#
|
||||
#
|
||||
# def test_adding_constraint_ex(self):
|
||||
# """Test adding canvas constraint for non-existing item"""
|
||||
# canvas = Canvas()
|
||||
# l1 = Line()
|
||||
# h1, h2 = l1.handles()
|
||||
# h = Handle()
|
||||
#
|
||||
# eq = EqualsConstraint(h1.x, h.x)
|
||||
# self.assertRaises(ValueError, canvas.add_canvas_constraint, l1, h1, eq)
|
||||
#
|
||||
#
|
||||
# def test_removing_constraint(self):
|
||||
# """Test removing canvas constraint"""
|
||||
# canvas = Canvas()
|
||||
# cons = canvas._canvas_constraints
|
||||
#
|
||||
# l1 = Line()
|
||||
# canvas.add(l1)
|
||||
#
|
||||
# h1, h2 = l1.handles()
|
||||
# h = Handle()
|
||||
#
|
||||
# eq1 = EqualsConstraint(h1.x, h.x)
|
||||
# canvas.add_canvas_constraint(l1, h1, eq1)
|
||||
#
|
||||
# # test preconditions
|
||||
# assert l1 in cons
|
||||
# assert h1 in cons[l1]
|
||||
# assert eq1 in cons[l1][h1]
|
||||
#
|
||||
# canvas.remove_canvas_constraint(l1, h1, eq1)
|
||||
# self.assertTrue(l1 in cons)
|
||||
# self.assertTrue(h1 in cons[l1])
|
||||
# self.assertFalse(eq1 in cons[l1][h1])
|
||||
#
|
||||
# eq1 = EqualsConstraint(h1.x, h.x)
|
||||
# eq2 = EqualsConstraint(h1.y, h.y)
|
||||
# canvas.add_canvas_constraint(l1, h1, eq1)
|
||||
# canvas.add_canvas_constraint(l1, h1, eq2)
|
||||
#
|
||||
# # test preconditions
|
||||
# assert l1 in cons
|
||||
# assert h1 in cons[l1]
|
||||
# assert eq1 in cons[l1][h1]
|
||||
# assert eq2 in cons[l1][h1]
|
||||
#
|
||||
# canvas.remove_canvas_constraint(l1, h1)
|
||||
#
|
||||
# self.assertTrue(l1 in cons)
|
||||
# self.assertTrue(h1 in cons[l1])
|
||||
# self.assertFalse(eq1 in cons[l1][h1])
|
||||
# self.assertFalse(eq2 in cons[l1][h1])
|
||||
#
|
||||
#
|
||||
# def test_fetching_constraints(self):
|
||||
# """Test fetching canvas constraints"""
|
||||
# canvas = Canvas()
|
||||
# cons = canvas._canvas_constraints
|
||||
#
|
||||
# l1 = Line()
|
||||
# canvas.add(l1)
|
||||
#
|
||||
# h1, h2 = l1.handles()
|
||||
# h = Handle()
|
||||
#
|
||||
# eq1 = EqualsConstraint(h1.x, h.x)
|
||||
# eq2 = EqualsConstraint(h1.y, h.y)
|
||||
# canvas.add_canvas_constraint(l1, h1, eq1)
|
||||
# canvas.add_canvas_constraint(l1, h1, eq2)
|
||||
#
|
||||
# # test preconditions
|
||||
# assert l1 in cons
|
||||
# assert h1 in cons[l1]
|
||||
# assert eq1 in cons[l1][h1]
|
||||
# assert eq2 in cons[l1][h1]
|
||||
#
|
||||
# self.assertTrue(eq1 in canvas.canvas_constraints(l1))
|
||||
# self.assertTrue(eq2 in canvas.canvas_constraints(l1))
|
||||
def test_disconnect_item_with_constraint_by_deleting_element():
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
line = Line()
|
||||
c = Canvas()
|
||||
c.add(b1)
|
||||
c.add(b2)
|
||||
c.add(line)
|
||||
|
||||
# vim:sw=4:et:ai
|
||||
cons = b1.ports()[0].constraint(c, line, line.handles()[0], b1)
|
||||
|
||||
c.connect_item(line, line.handles()[0], b1, b1.ports()[0], constraint=cons)
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 1
|
||||
|
||||
ncons = len(c.solver.constraints)
|
||||
assert ncons == 5
|
||||
|
||||
c.remove(b1)
|
||||
|
||||
assert count(c.get_connections(handle=line.handles()[0])) == 0
|
||||
|
||||
assert 2 == len(c.solver.constraints)
|
||||
|
||||
|
||||
def test_line_projection():
|
||||
"""Test projection with line's handle on element's side.
|
||||
|
||||
"""
|
||||
line = Line()
|
||||
line.matrix.translate(15, 50)
|
||||
h1, h2 = line.handles()
|
||||
h1.x, h1.y = 0, 0
|
||||
h2.x, h2.y = 20, 20
|
||||
|
||||
box = Box()
|
||||
box.matrix.translate(10, 10)
|
||||
box.width = 40
|
||||
box.height = 20
|
||||
|
||||
canvas = Canvas()
|
||||
canvas.add(line)
|
||||
canvas.add(box)
|
||||
|
||||
# move line's second handle on box side
|
||||
h2.x, h2.y = 5, -20
|
||||
|
||||
|
||||
def test_remove_connected_item():
|
||||
"""Test adding canvas constraint.
|
||||
|
||||
"""
|
||||
canvas = Canvas()
|
||||
|
||||
from gaphas.aspect import Connector, ConnectionSink
|
||||
|
||||
l1 = Line()
|
||||
canvas.add(l1)
|
||||
|
||||
b1 = Box()
|
||||
canvas.add(b1)
|
||||
|
||||
number_cons1 = len(canvas.solver.constraints)
|
||||
|
||||
b2 = Box()
|
||||
canvas.add(b2)
|
||||
|
||||
number_cons2 = len(canvas.solver.constraints)
|
||||
|
||||
conn = Connector(l1, l1.handles()[0])
|
||||
sink = ConnectionSink(b1, b1.ports()[0])
|
||||
|
||||
conn.connect(sink)
|
||||
|
||||
assert canvas.get_connection(l1.handles()[0])
|
||||
|
||||
conn = Connector(l1, l1.handles()[1])
|
||||
sink = ConnectionSink(b2, b2.ports()[0])
|
||||
|
||||
conn.connect(sink)
|
||||
|
||||
assert canvas.get_connection(l1.handles()[1])
|
||||
|
||||
assert number_cons2 + 2 == len(canvas.solver.constraints)
|
||||
|
||||
canvas.remove(b1)
|
||||
|
||||
# Expecting a class + line connected at one end only
|
||||
assert number_cons1 + 1 == len(canvas.solver.constraints)
|
||||
|
@ -1,37 +1,30 @@
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
from gaphas.connector import Position, Handle
|
||||
from gaphas.solver import Variable
|
||||
|
||||
|
||||
class PositionTestCase(unittest.TestCase):
|
||||
def test_position(self):
|
||||
pos = Position((0, 0))
|
||||
self.assertEqual(0, pos.x)
|
||||
self.assertEqual(0, pos.y)
|
||||
|
||||
def test_position(self):
|
||||
pos = Position((1, 2))
|
||||
self.assertEqual(1, pos.x)
|
||||
self.assertEqual(2, pos.y)
|
||||
|
||||
def test_set_xy(self):
|
||||
pos = Position((1, 2))
|
||||
x = Variable()
|
||||
y = Variable()
|
||||
assert x is not pos.x
|
||||
assert y is not pos.y
|
||||
|
||||
pos.set_x(x)
|
||||
pos.set_y(y)
|
||||
assert x is pos.x
|
||||
assert y is pos.y
|
||||
@pytest.mark.parametrize("position", [(0, 0), (1, 2)])
|
||||
def test_position(position):
|
||||
pos = Position(position)
|
||||
assert position[0] == pos.x
|
||||
assert position[1] == pos.y
|
||||
|
||||
|
||||
class HandleTestCase(unittest.TestCase):
|
||||
def test_handle_x_y(self):
|
||||
h = Handle()
|
||||
self.assertEqual(0.0, h.x)
|
||||
self.assertEqual(0.0, h.y)
|
||||
def test_set_xy():
|
||||
pos = Position((1, 2))
|
||||
x = Variable()
|
||||
y = Variable()
|
||||
assert x is not pos.x
|
||||
assert y is not pos.y
|
||||
|
||||
pos.set_x(x)
|
||||
pos.set_y(y)
|
||||
assert x is pos.x
|
||||
assert y is pos.y
|
||||
|
||||
|
||||
# vim: sw=4:et:ai
|
||||
def test_handle_x_y():
|
||||
h = Handle()
|
||||
assert 0.0 == h.x
|
||||
assert 0.0 == h.y
|
||||
|
@ -1,72 +1,62 @@
|
||||
import unittest
|
||||
|
||||
from gaphas.solver import Variable
|
||||
from gaphas.constraint import PositionConstraint, LineAlignConstraint
|
||||
|
||||
|
||||
class PositionTestCase(unittest.TestCase):
|
||||
def test_pos_constraint(self):
|
||||
"""Test position constraint"""
|
||||
x1, y1 = Variable(10), Variable(11)
|
||||
x2, y2 = Variable(12), Variable(13)
|
||||
pc = PositionConstraint(origin=(x1, y1), point=(x2, y2))
|
||||
pc.solve_for()
|
||||
def test_pos_constraint():
|
||||
"""Test position constraint"""
|
||||
x1, y1 = Variable(10), Variable(11)
|
||||
x2, y2 = Variable(12), Variable(13)
|
||||
pc = PositionConstraint(origin=(x1, y1), point=(x2, y2))
|
||||
pc.solve_for()
|
||||
|
||||
# origin shall remain the same
|
||||
self.assertEqual(10, x1)
|
||||
self.assertEqual(11, y1)
|
||||
# origin shall remain the same
|
||||
assert 10 == x1
|
||||
assert 11 == y1
|
||||
|
||||
# point shall be moved to origin
|
||||
self.assertEqual(10, x2)
|
||||
self.assertEqual(11, y2)
|
||||
# point shall be moved to origin
|
||||
assert 10 == x2
|
||||
assert 11 == y2
|
||||
|
||||
# change just x of origin
|
||||
x1.value = 15
|
||||
pc.solve_for()
|
||||
self.assertEqual(15, x2)
|
||||
# change just x of origin
|
||||
x1.value = 15
|
||||
pc.solve_for()
|
||||
assert 15 == x2
|
||||
|
||||
# change just y of origin
|
||||
y1.value = 14
|
||||
pc.solve_for()
|
||||
self.assertEqual(14, y2)
|
||||
# change just y of origin
|
||||
y1.value = 14
|
||||
pc.solve_for()
|
||||
assert 14 == y2
|
||||
|
||||
|
||||
class LineAlignConstraintTestCase(unittest.TestCase):
|
||||
def test_delta():
|
||||
"""Test line align constraint delta.
|
||||
"""
|
||||
Line align constraint test case.
|
||||
line = (Variable(0), Variable(0)), (Variable(30), Variable(20))
|
||||
point = (Variable(15), Variable(10))
|
||||
lc = LineAlignConstraint(line=line, point=point, align=0.5, delta=5)
|
||||
lc.solve_for()
|
||||
assert round(abs(19.16 - point[0].value), 2) == 0
|
||||
assert round(abs(12.77 - point[1].value), 2) == 0
|
||||
|
||||
line[1][0].value = 40
|
||||
line[1][1].value = 30
|
||||
lc.solve_for()
|
||||
assert round(abs(24.00 - point[0].value), 2) == 0
|
||||
assert round(abs(18.00 - point[1].value), 2) == 0
|
||||
|
||||
|
||||
def test_delta_below_zero():
|
||||
"""Test line align constraint with delta below zero.
|
||||
"""
|
||||
line = (Variable(0), Variable(0)), (Variable(30), Variable(20))
|
||||
point = (Variable(15), Variable(10))
|
||||
lc = LineAlignConstraint(line=line, point=point, align=0.5, delta=-5)
|
||||
lc.solve_for()
|
||||
assert round(abs(10.84 - point[0].value), 2) == 0
|
||||
assert round(abs(7.23 - point[1].value), 2) == 0
|
||||
|
||||
def test_delta(self):
|
||||
"""Test line align delta
|
||||
"""
|
||||
line = (Variable(0), Variable(0)), (Variable(30), Variable(20))
|
||||
point = (Variable(15), Variable(10))
|
||||
lc = LineAlignConstraint(line=line, point=point, align=0.5, delta=5)
|
||||
lc.solve_for()
|
||||
self.assertAlmostEqual(19.16, point[0].value, 2)
|
||||
self.assertAlmostEqual(12.77, point[1].value, 2)
|
||||
|
||||
line[1][0].value = 40
|
||||
line[1][1].value = 30
|
||||
lc.solve_for()
|
||||
self.assertAlmostEqual(24.00, point[0].value, 2)
|
||||
self.assertAlmostEqual(18.00, point[1].value, 2)
|
||||
|
||||
def test_delta_below_zero(self):
|
||||
"""Test line align with delta below zero
|
||||
"""
|
||||
line = (Variable(0), Variable(0)), (Variable(30), Variable(20))
|
||||
point = (Variable(15), Variable(10))
|
||||
lc = LineAlignConstraint(line=line, point=point, align=0.5, delta=-5)
|
||||
lc.solve_for()
|
||||
self.assertAlmostEqual(10.84, point[0].value, 2)
|
||||
self.assertAlmostEqual(7.23, point[1].value, 2)
|
||||
|
||||
line[1][0].value = 40
|
||||
line[1][1].value = 30
|
||||
lc.solve_for()
|
||||
self.assertAlmostEqual(16.0, point[0].value, 2)
|
||||
self.assertAlmostEqual(12.00, point[1].value, 2)
|
||||
|
||||
|
||||
# vim: sw=4:et:ai
|
||||
line[1][0].value = 40
|
||||
line[1][1].value = 30
|
||||
lc.solve_for()
|
||||
assert round(abs(16.0 - point[0].value), 2) == 0
|
||||
assert round(abs(12.00 - point[1].value), 2) == 0
|
||||
|
@ -1,103 +1,96 @@
|
||||
import pytest
|
||||
from builtins import range
|
||||
from os import getenv
|
||||
|
||||
import unittest
|
||||
|
||||
from gaphas.canvas import Canvas
|
||||
from gaphas.examples import Box
|
||||
|
||||
from gaphas.item import NW, NE, SE, SW
|
||||
|
||||
|
||||
class ElementTestCase(unittest.TestCase):
|
||||
def test_creation_with_size(self):
|
||||
"""
|
||||
Test if initial size holds when added to a canvas.
|
||||
"""
|
||||
canvas = Canvas()
|
||||
box = Box(150, 153)
|
||||
|
||||
assert box.width == 150, box.width
|
||||
assert box.height == 153, box.height
|
||||
assert box.handles()[SE].pos.x == 150, box.handles()[SE].pos.x
|
||||
assert box.handles()[SE].pos.y == 153, box.handles()[SE].pos.y
|
||||
|
||||
canvas.add(box)
|
||||
|
||||
assert box.width == 150, box.width
|
||||
assert box.height == 153, box.height
|
||||
assert box.handles()[SE].pos.x == 150, box.handles()[SE].pos.x
|
||||
assert box.handles()[SE].pos.y == 153, box.handles()[SE].pos.y
|
||||
|
||||
def test_resize_se(self):
|
||||
"""
|
||||
Test resizing of element by dragging it SE handle.
|
||||
"""
|
||||
canvas = Canvas()
|
||||
box = Box()
|
||||
handles = box.handles()
|
||||
|
||||
canvas.add(box)
|
||||
|
||||
h_nw, h_ne, h_se, h_sw = handles
|
||||
assert h_nw is handles[NW]
|
||||
assert h_ne is handles[NE]
|
||||
assert h_sw is handles[SW]
|
||||
assert h_se is handles[SE]
|
||||
|
||||
# to see how many solver was called:
|
||||
# GAPHAS_TEST_COUNT=3 nosetests -s --with-prof
|
||||
# --profile-restrict=gaphas gaphas/tests/test_element.py | grep -e
|
||||
# '\<solve\>' -e dirty
|
||||
|
||||
count = getenv("GAPHAS_TEST_COUNT")
|
||||
if count:
|
||||
count = int(count)
|
||||
else:
|
||||
count = 1
|
||||
|
||||
for i in range(count):
|
||||
h_se.pos.x += 100 # h.se.{x,y} = 10, now
|
||||
h_se.pos.y += 100
|
||||
box.request_update()
|
||||
canvas.update()
|
||||
|
||||
self.assertEqual(
|
||||
110 * count, h_se.pos.x
|
||||
) # h_se changed above, should remain the same
|
||||
self.assertEqual(110 * count, float(h_se.pos.y))
|
||||
|
||||
self.assertEqual(110 * count, float(h_ne.pos.x))
|
||||
self.assertEqual(110 * count, float(h_sw.pos.y))
|
||||
|
||||
def test_minimal_se(self):
|
||||
"""
|
||||
Test resizing of element by dragging it SE handle.
|
||||
"""
|
||||
canvas = Canvas()
|
||||
box = Box()
|
||||
handles = box.handles()
|
||||
|
||||
canvas.add(box)
|
||||
|
||||
h_nw, h_ne, h_se, h_sw = handles
|
||||
assert h_nw is handles[NW]
|
||||
assert h_ne is handles[NE]
|
||||
assert h_sw is handles[SW]
|
||||
assert h_se is handles[SE]
|
||||
|
||||
h_se.pos.x -= 20 # h.se.{x,y} == -10
|
||||
h_se.pos.y -= 20
|
||||
assert h_se.pos.x == h_se.pos.y == -10
|
||||
|
||||
box.request_update()
|
||||
canvas.update()
|
||||
|
||||
self.assertEqual(10, h_se.pos.x) # h_se changed above, should be 10
|
||||
self.assertEqual(10, h_se.pos.y)
|
||||
|
||||
self.assertEqual(10, h_ne.pos.x)
|
||||
self.assertEqual(10, h_sw.pos.y)
|
||||
class CanvasBox(object):
|
||||
def __init__(self):
|
||||
self.canvas = Canvas()
|
||||
self.box = Box()
|
||||
self.handles = self.box.handles()
|
||||
|
||||
|
||||
# vim:sw=4:et:ai
|
||||
@pytest.fixture()
|
||||
def cb():
|
||||
return CanvasBox()
|
||||
|
||||
|
||||
def test_creation_with_size(cb):
|
||||
"""Test if initial size holds when added to a canvas.
|
||||
|
||||
"""
|
||||
cb.box.width = 150
|
||||
cb.box.height = 153
|
||||
|
||||
assert cb.box.width == 150, cb.box.width
|
||||
assert cb.box.height == 153, cb.box.height
|
||||
assert cb.box.handles()[SE].pos.x == 150, cb.box.handles()[SE].pos.x
|
||||
assert cb.box.handles()[SE].pos.y == 153, cb.box.handles()[SE].pos.y
|
||||
|
||||
cb.canvas.add(cb.box)
|
||||
|
||||
assert cb.box.width == 150, cb.box.width
|
||||
assert cb.box.height == 153, cb.box.height
|
||||
assert cb.box.handles()[SE].pos.x == 150, cb.box.handles()[SE].pos.x
|
||||
assert cb.box.handles()[SE].pos.y == 153, cb.box.handles()[SE].pos.y
|
||||
|
||||
|
||||
def test_resize_se(cb):
|
||||
"""Test resizing of element by dragging its SE handle.
|
||||
|
||||
"""
|
||||
cb.canvas.add(cb.box)
|
||||
|
||||
h_nw, h_ne, h_se, h_sw = cb.handles
|
||||
assert h_nw is cb.handles[NW]
|
||||
assert h_ne is cb.handles[NE]
|
||||
assert h_sw is cb.handles[SW]
|
||||
assert h_se is cb.handles[SE]
|
||||
|
||||
count = getenv("GAPHAS_TEST_COUNT")
|
||||
if count:
|
||||
count = int(count)
|
||||
else:
|
||||
count = 1
|
||||
|
||||
for i in range(count):
|
||||
h_se.pos.x += 100 # h.se.{x,y} = 10, now
|
||||
h_se.pos.y += 100
|
||||
cb.box.request_update()
|
||||
cb.canvas.update()
|
||||
|
||||
assert 110 * count == h_se.pos.x # h_se changed above, should remain the same
|
||||
assert 110 * count == float(h_se.pos.y)
|
||||
|
||||
assert 110 * count == float(h_ne.pos.x)
|
||||
assert 110 * count == float(h_sw.pos.y)
|
||||
|
||||
|
||||
def test_minimal_se(cb):
|
||||
"""Test resizing of element by dragging its SE handle.
|
||||
|
||||
"""
|
||||
cb.canvas.add(cb.box)
|
||||
|
||||
h_nw, h_ne, h_se, h_sw = cb.handles
|
||||
assert h_nw is cb.handles[NW]
|
||||
assert h_ne is cb.handles[NE]
|
||||
assert h_sw is cb.handles[SW]
|
||||
assert h_se is cb.handles[SE]
|
||||
|
||||
h_se.pos.x -= 20 # h.se.{x,y} == -10
|
||||
h_se.pos.y -= 20
|
||||
assert h_se.pos.x == h_se.pos.y == -10
|
||||
|
||||
cb.box.request_update()
|
||||
cb.canvas.update()
|
||||
|
||||
assert 10 == h_se.pos.x # h_se changed above, should be 10
|
||||
assert 10 == h_se.pos.y
|
||||
|
||||
assert 10 == h_ne.pos.x
|
||||
assert 10 == h_sw.pos.y
|
||||
|
@ -1,43 +1,27 @@
|
||||
from __future__ import print_function
|
||||
|
||||
from builtins import object
|
||||
import unittest
|
||||
from gaphas.freehand import FreeHandCairoContext
|
||||
import cairo
|
||||
|
||||
|
||||
class PseudoFile(object):
|
||||
def __init__(self):
|
||||
self.data = ""
|
||||
|
||||
def write(self, data):
|
||||
self.data = self.data + data
|
||||
from gaphas.freehand import FreeHandCairoContext
|
||||
|
||||
|
||||
class FreeHandCairoContextTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
pass
|
||||
def test_drawing_lines():
|
||||
surface = cairo.SVGSurface("freehand-drawing-lines.svg", 100, 100)
|
||||
cr = FreeHandCairoContext(cairo.Context(surface))
|
||||
cr.set_line_width(2)
|
||||
cr.move_to(20, 20)
|
||||
cr.line_to(20, 80)
|
||||
cr.line_to(80, 80)
|
||||
cr.line_to(80, 20)
|
||||
cr.stroke()
|
||||
cr.show_page()
|
||||
|
||||
def test_drawing_lines(self):
|
||||
f = PseudoFile()
|
||||
|
||||
surface = cairo.SVGSurface("freehand-drawing-lines.svg", 100, 100)
|
||||
cr = FreeHandCairoContext(cairo.Context(surface))
|
||||
cr.set_line_width(2)
|
||||
cr.move_to(20, 20)
|
||||
cr.line_to(20, 80)
|
||||
cr.line_to(80, 80)
|
||||
cr.line_to(80, 20)
|
||||
cr.stroke()
|
||||
cr.show_page()
|
||||
|
||||
def test_drawing_rectangle(self):
|
||||
surface = cairo.SVGSurface("freehand-drawing-rectangle.svg", 100, 100)
|
||||
cr = FreeHandCairoContext(cairo.Context(surface))
|
||||
cr.set_line_width(2)
|
||||
cr.rectangle(20, 20, 60, 60)
|
||||
cr.stroke()
|
||||
cr.show_page()
|
||||
def test_drawing_rectangle():
|
||||
surface = cairo.SVGSurface("freehand-drawing-rectangle.svg", 100, 100)
|
||||
cr = FreeHandCairoContext(cairo.Context(surface))
|
||||
cr.set_line_width(2)
|
||||
cr.rectangle(20, 20, 60, 60)
|
||||
cr.stroke()
|
||||
cr.show_page()
|
||||
|
||||
|
||||
DRAWING_LINES_OUTPUT = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
@ -46,5 +30,3 @@ DRAWING_LINES_OUTPUT = """<?xml version="1.0" encoding="UTF-8"?>
|
||||
<path style="fill:none;stroke-width:2;stroke-linecap:butt;stroke-linejoin:miter;stroke:rgb(0%,0%,0%);stroke-opacity:1;stroke-miterlimit:10;" d="M 20 20 C 23.324219 50.054688 17.195312 33.457031 20.722656 80.585938 C 38.78125 83.566406 20.984375 77.625 80.652344 80.652344 C 83.65625 70.992188 77.578125 60.988281 80.507812 20.019531 "/>
|
||||
</g>
|
||||
</svg>"""
|
||||
|
||||
# vim:sw=4:et:ai
|
||||
|
@ -1,8 +1,8 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import unittest
|
||||
from builtins import range
|
||||
|
||||
import pytest
|
||||
from gi.repository import Gtk
|
||||
|
||||
from gaphas.canvas import Canvas
|
||||
@ -11,161 +11,157 @@ from gaphas.item import Element, Line
|
||||
from gaphas.view import GtkView
|
||||
|
||||
|
||||
class GuideTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
class TestWindow(object):
|
||||
def __init__(self):
|
||||
self.canvas = Canvas()
|
||||
self.view = GtkView(self.canvas)
|
||||
self.window = Gtk.Window()
|
||||
self.window.add(self.view)
|
||||
self.window.show_all()
|
||||
|
||||
def tearDown(self):
|
||||
self.window.destroy()
|
||||
|
||||
def test_find_closest(self):
|
||||
"""
|
||||
test find closest method.
|
||||
"""
|
||||
set1 = [0, 10, 20]
|
||||
set2 = [2, 15, 30]
|
||||
|
||||
guider = GuidedItemInMotion(Element(), self.view)
|
||||
d, closest = guider.find_closest(set1, set2)
|
||||
self.assertEqual(2.0, d)
|
||||
self.assertEqual([2.0], closest)
|
||||
|
||||
def test_element_guide(self):
|
||||
e1 = Element()
|
||||
self.assertEqual(10, e1.width)
|
||||
self.assertEqual(10, e1.height)
|
||||
guides = Guide(e1).horizontal()
|
||||
self.assertEqual(0.0, guides[0])
|
||||
self.assertEqual(5.0, guides[1])
|
||||
self.assertEqual(10.0, guides[2])
|
||||
guides = Guide(e1).vertical()
|
||||
self.assertEqual(0.0, guides[0])
|
||||
self.assertEqual(5.0, guides[1])
|
||||
self.assertEqual(10.0, guides[2])
|
||||
|
||||
def test_line_guide(self):
|
||||
c = Canvas()
|
||||
line = Line()
|
||||
c.add(line)
|
||||
line.handles().append(line._create_handle((20, 20)))
|
||||
line.handles().append(line._create_handle((30, 30)))
|
||||
line.handles().append(line._create_handle((40, 40)))
|
||||
line.orthogonal = True
|
||||
c.update_now()
|
||||
|
||||
guides = list(Guide(line).horizontal())
|
||||
self.assertEqual(2, len(guides))
|
||||
self.assertEqual(10.0, guides[0])
|
||||
self.assertEqual(40.0, guides[1])
|
||||
|
||||
guides = list(Guide(line).vertical())
|
||||
self.assertEqual(2, len(guides))
|
||||
self.assertEqual(00.0, guides[0])
|
||||
self.assertEqual(20.0, guides[1])
|
||||
|
||||
def test_line_guide_horizontal(self):
|
||||
c = Canvas()
|
||||
line = Line()
|
||||
c.add(line)
|
||||
line.handles().append(line._create_handle((20, 20)))
|
||||
line.handles().append(line._create_handle((30, 30)))
|
||||
line.handles().append(line._create_handle((40, 40)))
|
||||
line.horizontal = True
|
||||
line.orthogonal = True
|
||||
c.update_now()
|
||||
|
||||
guides = list(Guide(line).horizontal())
|
||||
self.assertEqual(2, len(guides))
|
||||
self.assertEqual(0.0, guides[0])
|
||||
self.assertEqual(20.0, guides[1])
|
||||
|
||||
guides = list(Guide(line).horizontal())
|
||||
self.assertEqual(2, len(guides))
|
||||
self.assertEqual(0.0, guides[0])
|
||||
self.assertEqual(20.0, guides[1])
|
||||
|
||||
def test_guide_item_in_motion(self):
|
||||
e1 = Element()
|
||||
e2 = Element()
|
||||
e3 = Element()
|
||||
|
||||
canvas = self.canvas
|
||||
canvas.add(e1)
|
||||
canvas.add(e2)
|
||||
canvas.add(e3)
|
||||
|
||||
self.assertEqual(0, e1.matrix[4])
|
||||
self.assertEqual(0, e1.matrix[5])
|
||||
|
||||
e2.matrix.translate(40, 40)
|
||||
e2.request_update()
|
||||
self.assertEqual(40, e2.matrix[4])
|
||||
self.assertEqual(40, e2.matrix[5])
|
||||
|
||||
guider = GuidedItemInMotion(e3, self.view)
|
||||
|
||||
guider.start_move((0, 0))
|
||||
self.assertEqual(0, e3.matrix[4])
|
||||
self.assertEqual(0, e3.matrix[5])
|
||||
|
||||
# Moved back to guided lines:
|
||||
for d in range(0, 3):
|
||||
guider.move((d, d))
|
||||
self.assertEqual(0, e3.matrix[4])
|
||||
self.assertEqual(0, e3.matrix[5])
|
||||
|
||||
for d in range(3, 5):
|
||||
guider.move((d, d))
|
||||
self.assertEqual(5, e3.matrix[4])
|
||||
self.assertEqual(5, e3.matrix[5])
|
||||
|
||||
guider.move((20, 20))
|
||||
self.assertEqual(20, e3.matrix[4])
|
||||
self.assertEqual(20, e3.matrix[5])
|
||||
|
||||
def test_guide_item_in_motion_2(self):
|
||||
e1 = Element()
|
||||
e2 = Element()
|
||||
e3 = Element()
|
||||
|
||||
canvas = self.canvas
|
||||
canvas.add(e1)
|
||||
canvas.add(e2)
|
||||
canvas.add(e3)
|
||||
|
||||
self.assertEqual(0, e1.matrix[4])
|
||||
self.assertEqual(0, e1.matrix[5])
|
||||
|
||||
e2.matrix.translate(40, 40)
|
||||
e2.request_update()
|
||||
self.assertEqual(40, e2.matrix[4])
|
||||
self.assertEqual(40, e2.matrix[5])
|
||||
|
||||
guider = GuidedItemInMotion(e3, self.view)
|
||||
|
||||
guider.start_move((3, 3))
|
||||
self.assertEqual(0, e3.matrix[4])
|
||||
self.assertEqual(0, e3.matrix[5])
|
||||
|
||||
# Moved back to guided lines:
|
||||
for y in range(4, 6):
|
||||
guider.move((3, y))
|
||||
self.assertEqual(0, e3.matrix[4])
|
||||
self.assertEqual(0, e3.matrix[5])
|
||||
|
||||
for y in range(6, 9):
|
||||
guider.move((3, y))
|
||||
self.assertEqual(0, e3.matrix[4])
|
||||
self.assertEqual(5, e3.matrix[5])
|
||||
|
||||
# Take into account initial cursor offset of (3, 3)
|
||||
guider.move((20, 23))
|
||||
self.assertEqual(17, e3.matrix[4])
|
||||
self.assertEqual(20, e3.matrix[5])
|
||||
self.line = Line()
|
||||
self.canvas.add(self.line)
|
||||
self.e1 = Element()
|
||||
self.e2 = Element()
|
||||
self.e3 = Element()
|
||||
|
||||
|
||||
# vim:sw=4:et:ai
|
||||
@pytest.fixture()
|
||||
def win():
|
||||
test_window = TestWindow()
|
||||
yield test_window
|
||||
test_window.window.destroy()
|
||||
|
||||
|
||||
def test_find_closest(win):
|
||||
"""Test find closest method.
|
||||
|
||||
"""
|
||||
set1 = [0, 10, 20]
|
||||
set2 = [2, 15, 30]
|
||||
|
||||
guider = GuidedItemInMotion(Element(), win.view)
|
||||
d, closest = guider.find_closest(set1, set2)
|
||||
assert 2.0 == d
|
||||
assert [2.0] == closest
|
||||
|
||||
|
||||
def test_element_guide():
|
||||
e1 = Element()
|
||||
assert 10 == e1.width
|
||||
assert 10 == e1.height
|
||||
guides = Guide(e1).horizontal()
|
||||
assert 0.0 == guides[0]
|
||||
assert 5.0 == guides[1]
|
||||
assert 10.0 == guides[2]
|
||||
guides = Guide(e1).vertical()
|
||||
assert 0.0 == guides[0]
|
||||
assert 5.0 == guides[1]
|
||||
assert 10.0 == guides[2]
|
||||
|
||||
|
||||
def test_line_guide(win):
|
||||
win.line.handles().append(win.line._create_handle((20, 20)))
|
||||
win.line.handles().append(win.line._create_handle((30, 30)))
|
||||
win.line.handles().append(win.line._create_handle((40, 40)))
|
||||
win.line.orthogonal = True
|
||||
win.canvas.update_now()
|
||||
|
||||
guides = list(Guide(win.line).horizontal())
|
||||
assert 2 == len(guides)
|
||||
assert 10.0 == guides[0]
|
||||
assert 40.0 == guides[1]
|
||||
|
||||
guides = list(Guide(win.line).vertical())
|
||||
assert 2 == len(guides)
|
||||
assert 00.0 == guides[0]
|
||||
assert 20.0 == guides[1]
|
||||
|
||||
|
||||
def test_line_guide_horizontal(win):
|
||||
win.line.handles().append(win.line._create_handle((20, 20)))
|
||||
win.line.handles().append(win.line._create_handle((30, 30)))
|
||||
win.line.handles().append(win.line._create_handle((40, 40)))
|
||||
win.line.horizontal = True
|
||||
win.line.orthogonal = True
|
||||
win.canvas.update_now()
|
||||
|
||||
guides = list(Guide(win.line).horizontal())
|
||||
assert 2 == len(guides)
|
||||
assert 0.0 == guides[0]
|
||||
assert 20.0 == guides[1]
|
||||
|
||||
guides = list(Guide(win.line).horizontal())
|
||||
assert 2 == len(guides)
|
||||
assert 0.0 == guides[0]
|
||||
assert 20.0 == guides[1]
|
||||
|
||||
|
||||
def test_guide_item_in_motion(win):
|
||||
win.canvas.add(win.e1)
|
||||
win.canvas.add(win.e2)
|
||||
win.canvas.add(win.e3)
|
||||
|
||||
assert 0 == win.e1.matrix[4]
|
||||
assert 0 == win.e1.matrix[5]
|
||||
|
||||
win.e2.matrix.translate(40, 40)
|
||||
win.e2.request_update()
|
||||
assert 40 == win.e2.matrix[4]
|
||||
assert 40 == win.e2.matrix[5]
|
||||
|
||||
guider = GuidedItemInMotion(win.e3, win.view)
|
||||
|
||||
guider.start_move((0, 0))
|
||||
assert 0 == win.e3.matrix[4]
|
||||
assert 0 == win.e3.matrix[5]
|
||||
|
||||
# Moved back to guided lines:
|
||||
for d in range(0, 3):
|
||||
guider.move((d, d))
|
||||
assert 0 == win.e3.matrix[4]
|
||||
assert 0 == win.e3.matrix[5]
|
||||
|
||||
for d in range(3, 5):
|
||||
guider.move((d, d))
|
||||
assert 5 == win.e3.matrix[4]
|
||||
assert 5 == win.e3.matrix[5]
|
||||
|
||||
guider.move((20, 20))
|
||||
assert 20 == win.e3.matrix[4]
|
||||
assert 20 == win.e3.matrix[5]
|
||||
|
||||
|
||||
def test_guide_item_in_motion_2(win):
|
||||
win.canvas.add(win.e1)
|
||||
win.canvas.add(win.e2)
|
||||
win.canvas.add(win.e3)
|
||||
|
||||
assert 0 == win.e1.matrix[4]
|
||||
assert 0 == win.e1.matrix[5]
|
||||
|
||||
win.e2.matrix.translate(40, 40)
|
||||
win.e2.request_update()
|
||||
assert 40 == win.e2.matrix[4]
|
||||
assert 40 == win.e2.matrix[5]
|
||||
|
||||
guider = GuidedItemInMotion(win.e3, win.view)
|
||||
|
||||
guider.start_move((3, 3))
|
||||
assert 0 == win.e3.matrix[4]
|
||||
assert 0 == win.e3.matrix[5]
|
||||
|
||||
# Moved back to guided lines:
|
||||
for y in range(4, 6):
|
||||
guider.move((3, y))
|
||||
assert 0 == win.e3.matrix[4]
|
||||
assert 0 == win.e3.matrix[5]
|
||||
|
||||
for y in range(6, 9):
|
||||
guider.move((3, y))
|
||||
assert 0 == win.e3.matrix[4]
|
||||
assert 5 == win.e3.matrix[5]
|
||||
|
||||
# Take into account initial cursor offset of (3, 3)
|
||||
guider.move((20, 23))
|
||||
assert 17 == win.e3.matrix[4]
|
||||
assert 20 == win.e3.matrix[5]
|
||||
|
@ -1,93 +1,91 @@
|
||||
"""
|
||||
Generic gaphas item tests.
|
||||
"""
|
||||
"""Item constraint creation tests.
|
||||
|
||||
import unittest
|
||||
The test check functionality of `Item.constraint` method, not constraints
|
||||
themselves.
|
||||
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from gaphas.item import Item
|
||||
from gaphas.constraint import LineConstraint, EqualsConstraint, LessThanConstraint
|
||||
from gaphas.item import Item
|
||||
from gaphas.solver import Variable
|
||||
|
||||
|
||||
class ItemConstraintTestCase(unittest.TestCase):
|
||||
class ItemPosition(object):
|
||||
def __init__(self):
|
||||
self.item = Item()
|
||||
self.pos1 = Variable(1), Variable(2)
|
||||
self.pos2 = Variable(3), Variable(4)
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def item_pos():
|
||||
return ItemPosition()
|
||||
|
||||
|
||||
def test_line_constraint(item_pos):
|
||||
"""Test line creation constraint.
|
||||
|
||||
"""
|
||||
Item constraint creation tests. The test check functionality of
|
||||
`Item.constraint` method, not constraints themselves.
|
||||
line = (Variable(3), Variable(4)), (Variable(5), Variable(6))
|
||||
item_pos.item.constraint(line=(item_pos.pos1, line))
|
||||
assert 1 == len(item_pos.item._constraints)
|
||||
|
||||
c = item_pos.item._constraints[0]
|
||||
assert isinstance(c, LineConstraint)
|
||||
assert (1, 2) == c._point
|
||||
assert ((3, 4), (5, 6)) == c._line
|
||||
|
||||
|
||||
def test_horizontal_constraint(item_pos):
|
||||
"""Test horizontal constraint creation.
|
||||
|
||||
"""
|
||||
item_pos.item.constraint(horizontal=(item_pos.pos1, item_pos.pos2))
|
||||
assert 1 == len(item_pos.item._constraints)
|
||||
|
||||
def test_line_constraint(self):
|
||||
"""
|
||||
Test line creation constraint.
|
||||
"""
|
||||
item = Item()
|
||||
pos = Variable(1), Variable(2)
|
||||
line = (Variable(3), Variable(4)), (Variable(5), Variable(6))
|
||||
item.constraint(line=(pos, line))
|
||||
self.assertEqual(1, len(item._constraints))
|
||||
c = item_pos.item._constraints[0]
|
||||
assert isinstance(c, EqualsConstraint)
|
||||
# Expect constraint on y-axis
|
||||
assert 2 == c.a
|
||||
assert 4 == c.b
|
||||
|
||||
c = item._constraints[0]
|
||||
self.assertTrue(isinstance(c, LineConstraint))
|
||||
self.assertEqual((1, 2), c._point)
|
||||
self.assertEqual(((3, 4), (5, 6)), c._line)
|
||||
|
||||
def test_horizontal_constraint(self):
|
||||
"""
|
||||
Test horizontal constraint creation.
|
||||
"""
|
||||
item = Item()
|
||||
p1 = Variable(1), Variable(2)
|
||||
p2 = Variable(3), Variable(4)
|
||||
item.constraint(horizontal=(p1, p2))
|
||||
self.assertEqual(1, len(item._constraints))
|
||||
def test_vertical_constraint(item_pos):
|
||||
"""Test vertical constraint creation.
|
||||
|
||||
c = item._constraints[0]
|
||||
self.assertTrue(isinstance(c, EqualsConstraint))
|
||||
# expect constraint on y-axis
|
||||
self.assertEqual(2, c.a)
|
||||
self.assertEqual(4, c.b)
|
||||
"""
|
||||
item_pos.item.constraint(vertical=(item_pos.pos1, item_pos.pos2))
|
||||
assert 1 == len(item_pos.item._constraints)
|
||||
|
||||
def test_vertical_constraint(self):
|
||||
"""
|
||||
Test vertical constraint creation.
|
||||
"""
|
||||
item = Item()
|
||||
p1 = Variable(1), Variable(2)
|
||||
p2 = Variable(3), Variable(4)
|
||||
item.constraint(vertical=(p1, p2))
|
||||
self.assertEqual(1, len(item._constraints))
|
||||
c = item_pos.item._constraints[0]
|
||||
assert isinstance(c, EqualsConstraint)
|
||||
# Expect constraint on x-axis
|
||||
assert 1 == c.a
|
||||
assert 3 == c.b
|
||||
|
||||
c = item._constraints[0]
|
||||
self.assertTrue(isinstance(c, EqualsConstraint))
|
||||
# expect constraint on x-axis
|
||||
self.assertEqual(1, c.a)
|
||||
self.assertEqual(3, c.b)
|
||||
|
||||
def test_left_of_constraint(self):
|
||||
"""
|
||||
Test "less than" constraint (horizontal) creation.
|
||||
"""
|
||||
item = Item()
|
||||
p1 = Variable(1), Variable(2)
|
||||
p2 = Variable(3), Variable(4)
|
||||
item.constraint(left_of=(p1, p2))
|
||||
self.assertEqual(1, len(item._constraints))
|
||||
def test_left_of_constraint(item_pos):
|
||||
"""Test "less than" constraint (horizontal) creation.
|
||||
|
||||
c = item._constraints[0]
|
||||
self.assertTrue(isinstance(c, LessThanConstraint))
|
||||
self.assertEqual(1, c.smaller)
|
||||
self.assertEqual(3, c.bigger)
|
||||
"""
|
||||
item_pos.item.constraint(left_of=(item_pos.pos1, item_pos.pos2))
|
||||
assert 1 == len(item_pos.item._constraints)
|
||||
|
||||
def test_above_constraint(self):
|
||||
"""
|
||||
Test "less than" constraint (vertical) creation.
|
||||
"""
|
||||
item = Item()
|
||||
p1 = Variable(1), Variable(2)
|
||||
p2 = Variable(3), Variable(4)
|
||||
item.constraint(above=(p1, p2))
|
||||
self.assertEqual(1, len(item._constraints))
|
||||
c = item_pos.item._constraints[0]
|
||||
assert isinstance(c, LessThanConstraint)
|
||||
assert 1 == c.smaller
|
||||
assert 3 == c.bigger
|
||||
|
||||
c = item._constraints[0]
|
||||
self.assertTrue(isinstance(c, LessThanConstraint))
|
||||
self.assertEqual(2, c.smaller)
|
||||
self.assertEqual(4, c.bigger)
|
||||
|
||||
def test_above_constraint(item_pos):
|
||||
"""
|
||||
Test "less than" constraint (vertical) creation.
|
||||
"""
|
||||
item_pos.item.constraint(above=(item_pos.pos1, item_pos.pos2))
|
||||
assert 1 == len(item_pos.item._constraints)
|
||||
|
||||
c = item_pos.item._constraints[0]
|
||||
assert isinstance(c, LessThanConstraint)
|
||||
assert 2 == c.smaller
|
||||
assert 4 == c.bigger
|
||||
|
@ -1,109 +1,75 @@
|
||||
import unittest
|
||||
from gaphas.item import Line
|
||||
"""Basic item tests for lines.
|
||||
|
||||
"""
|
||||
|
||||
from gaphas.canvas import Canvas
|
||||
from gaphas import state
|
||||
from gaphas.item import Line
|
||||
from gaphas.segment import Segment
|
||||
|
||||
|
||||
undo_list = []
|
||||
redo_list = []
|
||||
def test_initial_ports(revert_undo):
|
||||
"""Test initial ports amount.
|
||||
|
||||
|
||||
def undo_handler(event):
|
||||
undo_list.append(event)
|
||||
|
||||
|
||||
def undo():
|
||||
apply_me = list(undo_list)
|
||||
del undo_list[:]
|
||||
apply_me.reverse()
|
||||
for e in apply_me:
|
||||
state.saveapply(*e)
|
||||
redo_list[:] = undo_list[:]
|
||||
del undo_list[:]
|
||||
|
||||
|
||||
class TestCaseBase(unittest.TestCase):
|
||||
"""
|
||||
Abstract test case class with undo support.
|
||||
line = Line()
|
||||
assert 1 == len(line.ports())
|
||||
|
||||
|
||||
def test_orthogonal_horizontal_undo(revert_undo, undo_fixture):
|
||||
"""Test orthogonal line constraints bug (#107).
|
||||
|
||||
"""
|
||||
canvas = Canvas()
|
||||
line = Line()
|
||||
canvas.add(line)
|
||||
assert not line.horizontal
|
||||
assert len(canvas.solver._constraints) == 0
|
||||
|
||||
def setUp(self):
|
||||
state.observers.add(state.revert_handler)
|
||||
state.subscribers.add(undo_handler)
|
||||
segment = Segment(line, None)
|
||||
segment.split_segment(0)
|
||||
|
||||
def tearDown(self):
|
||||
state.observers.remove(state.revert_handler)
|
||||
state.subscribers.remove(undo_handler)
|
||||
line.orthogonal = True
|
||||
|
||||
assert 2 == len(canvas.solver._constraints)
|
||||
|
||||
del undo_fixture[2][:] # Clear undo_list
|
||||
line.horizontal = True
|
||||
|
||||
assert 2 == len(canvas.solver._constraints)
|
||||
|
||||
undo_fixture[0]() # Call undo
|
||||
|
||||
assert not line.horizontal
|
||||
assert 2 == len(canvas.solver._constraints)
|
||||
|
||||
line.horizontal = True
|
||||
|
||||
assert line.horizontal
|
||||
assert 2 == len(canvas.solver._constraints)
|
||||
|
||||
|
||||
class LineTestCase(TestCaseBase):
|
||||
"""
|
||||
Basic line item tests.
|
||||
def test_orthogonal_line_undo(revert_undo, undo_fixture):
|
||||
"""Test orthogonal line undo.
|
||||
|
||||
"""
|
||||
canvas = Canvas()
|
||||
line = Line()
|
||||
canvas.add(line)
|
||||
|
||||
def test_initial_ports(self):
|
||||
"""Test initial ports amount
|
||||
"""
|
||||
line = Line()
|
||||
self.assertEqual(1, len(line.ports()))
|
||||
segment = Segment(line, None)
|
||||
segment.split_segment(0)
|
||||
|
||||
def test_orthogonal_horizontal_undo(self):
|
||||
"""Test orthogonal line constraints bug (#107)
|
||||
"""
|
||||
canvas = Canvas()
|
||||
line = Line()
|
||||
canvas.add(line)
|
||||
assert not line.horizontal
|
||||
assert len(canvas.solver._constraints) == 0
|
||||
# Start with no orthogonal constraints
|
||||
assert len(canvas.solver._constraints) == 0
|
||||
|
||||
segment = Segment(line, None)
|
||||
segment.split_segment(0)
|
||||
line.orthogonal = True
|
||||
|
||||
line.orthogonal = True
|
||||
# Check orthogonal constraints
|
||||
assert 2 == len(canvas.solver._constraints)
|
||||
assert 3 == len(line.handles())
|
||||
|
||||
self.assertEqual(2, len(canvas.solver._constraints))
|
||||
after_ortho = set(canvas.solver._constraints)
|
||||
undo_fixture[0]() # Call undo
|
||||
|
||||
del undo_list[:]
|
||||
line.horizontal = True
|
||||
|
||||
self.assertEqual(2, len(canvas.solver._constraints))
|
||||
|
||||
undo()
|
||||
|
||||
self.assertFalse(line.horizontal)
|
||||
self.assertEqual(2, len(canvas.solver._constraints))
|
||||
|
||||
line.horizontal = True
|
||||
|
||||
self.assertTrue(line.horizontal)
|
||||
self.assertEqual(2, len(canvas.solver._constraints))
|
||||
|
||||
def test_orthogonal_line_undo(self):
|
||||
"""Test orthogonal line undo
|
||||
"""
|
||||
canvas = Canvas()
|
||||
line = Line()
|
||||
canvas.add(line)
|
||||
|
||||
segment = Segment(line, None)
|
||||
segment.split_segment(0)
|
||||
|
||||
# start with no orthogonal constraints
|
||||
assert len(canvas.solver._constraints) == 0
|
||||
|
||||
line.orthogonal = True
|
||||
|
||||
# check orthogonal constraints
|
||||
self.assertEqual(2, len(canvas.solver._constraints))
|
||||
self.assertEqual(3, len(line.handles()))
|
||||
|
||||
undo()
|
||||
|
||||
self.assertFalse(line.orthogonal)
|
||||
self.assertEqual(0, len(canvas.solver._constraints))
|
||||
self.assertEqual(2, len(line.handles()))
|
||||
|
||||
|
||||
# vim:sw=4:et
|
||||
assert not line.orthogonal
|
||||
assert 0 == len(canvas.solver._constraints)
|
||||
assert 2 == len(line.handles())
|
||||
|
@ -1,26 +1,25 @@
|
||||
from __future__ import print_function
|
||||
|
||||
import io
|
||||
import pickle
|
||||
import unittest
|
||||
from builtins import object
|
||||
|
||||
import cairo
|
||||
import pytest
|
||||
from future import standard_library
|
||||
from gi.repository import Gtk
|
||||
|
||||
import demo
|
||||
from gaphas.canvas import Canvas
|
||||
from gaphas.examples import Box
|
||||
from gaphas.item import Element, Line
|
||||
from gaphas.view import View, GtkView
|
||||
|
||||
# Ensure extra pickle reducers/reconstructors are loaded:
|
||||
import gaphas.picklers
|
||||
|
||||
standard_library.install_aliases()
|
||||
|
||||
|
||||
class MyPickler(pickle.Pickler):
|
||||
def save(self, obj):
|
||||
# print('saving obj', obj, type(obj))
|
||||
def save(self, obj, save_persistent_id=True):
|
||||
try:
|
||||
return pickle.Pickler.save(self, obj)
|
||||
except pickle.PicklingError as e:
|
||||
@ -29,191 +28,167 @@ class MyPickler(pickle.Pickler):
|
||||
|
||||
|
||||
class MyDisconnect(object):
|
||||
"""
|
||||
Disconnect object should be located at top-level, so the pickle code
|
||||
"""Create a disconnect object.
|
||||
|
||||
The disconnect object should be located at top-level, so the pickle code
|
||||
can find it.
|
||||
|
||||
"""
|
||||
|
||||
def __call__(self):
|
||||
pass
|
||||
|
||||
|
||||
def create_canvas():
|
||||
canvas = Canvas()
|
||||
box = Box()
|
||||
canvas.add(box)
|
||||
box.matrix.translate(100, 50)
|
||||
box.matrix.rotate(50)
|
||||
box2 = Box()
|
||||
canvas.add(box2, parent=box)
|
||||
|
||||
line = Line()
|
||||
line.handles()[0].visible = False
|
||||
line.handles()[0].connected_to = box
|
||||
line.handles()[0].disconnect = MyDisconnect()
|
||||
line.handles()[0].connection_data = 1
|
||||
|
||||
canvas.add(line)
|
||||
|
||||
canvas.update()
|
||||
|
||||
return canvas
|
||||
class CanvasFixture(object):
|
||||
def __init__(self):
|
||||
self.canvas = Canvas()
|
||||
self.box = Box()
|
||||
self.box2 = Box()
|
||||
self.line = Line()
|
||||
|
||||
|
||||
class PickleTestCase(unittest.TestCase):
|
||||
def test_pickle_element(self):
|
||||
item = Element()
|
||||
@pytest.fixture()
|
||||
def canvas_fixture():
|
||||
cf = CanvasFixture()
|
||||
cf.canvas.add(cf.box)
|
||||
cf.box.matrix.translate(100, 50)
|
||||
cf.box.matrix.rotate(50)
|
||||
cf.canvas.add(cf.box2, parent=cf.box)
|
||||
|
||||
pickled = pickle.dumps(item)
|
||||
i2 = pickle.loads(pickled)
|
||||
cf.line.handles()[0].visible = False
|
||||
cf.line.handles()[0].connected_to = cf.box
|
||||
cf.line.handles()[0].disconnect = MyDisconnect()
|
||||
cf.line.handles()[0].connection_data = 1
|
||||
|
||||
assert i2
|
||||
assert len(i2.handles()) == 4
|
||||
cf.canvas.add(cf.line)
|
||||
|
||||
def test_pickle_line(self):
|
||||
item = Line()
|
||||
cf.canvas.update()
|
||||
|
||||
pickled = pickle.dumps(item)
|
||||
i2 = pickle.loads(pickled)
|
||||
|
||||
assert i2
|
||||
assert len(i2.handles()) == 2
|
||||
|
||||
def test_pickle(self):
|
||||
canvas = create_canvas()
|
||||
|
||||
pickled = pickle.dumps(canvas)
|
||||
c2 = pickle.loads(pickled)
|
||||
|
||||
assert type(canvas._tree.nodes[0]) is Box
|
||||
assert type(canvas._tree.nodes[1]) is Box
|
||||
assert type(canvas._tree.nodes[2]) is Line
|
||||
|
||||
def test_pickle_connect(self):
|
||||
"""
|
||||
Persist a connection.
|
||||
"""
|
||||
canvas = Canvas()
|
||||
box = Box()
|
||||
canvas.add(box)
|
||||
box2 = Box()
|
||||
canvas.add(box2, parent=box)
|
||||
|
||||
line = Line()
|
||||
line.handles()[0].visible = False
|
||||
line.handles()[0].connected_to = box
|
||||
line.handles()[0].disconnect = MyDisconnect()
|
||||
line.handles()[0].connection_data = 1
|
||||
|
||||
canvas.add(line)
|
||||
|
||||
pickled = pickle.dumps(canvas)
|
||||
c2 = pickle.loads(pickled)
|
||||
|
||||
assert type(canvas._tree.nodes[0]) is Box
|
||||
assert type(canvas._tree.nodes[1]) is Box
|
||||
assert type(canvas._tree.nodes[2]) is Line
|
||||
assert c2.solver
|
||||
|
||||
line2 = c2._tree.nodes[2]
|
||||
h = line2.handles()[0]
|
||||
assert h.visible == False
|
||||
assert h.connected_to is c2._tree.nodes[0]
|
||||
|
||||
# connection_data and disconnect have not been persisted
|
||||
assert h.connection_data == 1, h.connection_data
|
||||
assert h.disconnect, h.disconnect
|
||||
assert callable(h.disconnect)
|
||||
assert h.disconnect() is None, h.disconnect()
|
||||
|
||||
def test_pickle_with_view(self):
|
||||
canvas = create_canvas()
|
||||
|
||||
pickled = pickle.dumps(canvas)
|
||||
|
||||
c2 = pickle.loads(pickled)
|
||||
|
||||
view = View(canvas=c2)
|
||||
|
||||
import cairo
|
||||
|
||||
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)
|
||||
cr = cairo.Context(surface)
|
||||
view.update_bounding_box(cr)
|
||||
cr.show_page()
|
||||
surface.flush()
|
||||
surface.finish()
|
||||
|
||||
def test_pickle_with_gtk_view(self):
|
||||
canvas = create_canvas()
|
||||
|
||||
pickled = pickle.dumps(canvas)
|
||||
|
||||
c2 = pickle.loads(pickled)
|
||||
|
||||
win = Gtk.Window()
|
||||
view = GtkView(canvas=c2)
|
||||
win.add(view)
|
||||
|
||||
view.show()
|
||||
win.show()
|
||||
|
||||
view.update()
|
||||
|
||||
def test_pickle_with_gtk_view_with_connection(self):
|
||||
canvas = create_canvas()
|
||||
box = canvas._tree.nodes[0]
|
||||
assert isinstance(box, Box)
|
||||
line = canvas._tree.nodes[2]
|
||||
assert isinstance(line, Line)
|
||||
|
||||
view = GtkView(canvas=canvas)
|
||||
|
||||
# from gaphas.tool import ConnectHandleTool
|
||||
# handle_tool = ConnectHandleTool()
|
||||
# handle_tool.connect(view, line, line.handles()[0], (40, 0))
|
||||
# assert line.handles()[0].connected_to is box, line.handles()[0].connected_to
|
||||
# assert line.handles()[0].connection_data
|
||||
# assert line.handles()[0].disconnect
|
||||
# assert isinstance(line.handles()[0].disconnect, object), line.handles()[0].disconnect
|
||||
|
||||
import io
|
||||
|
||||
f = io.BytesIO()
|
||||
pickler = MyPickler(f)
|
||||
pickler.dump(canvas)
|
||||
pickled = f.getvalue()
|
||||
|
||||
c2 = pickle.loads(pickled)
|
||||
|
||||
win = Gtk.Window()
|
||||
view = GtkView(canvas=c2)
|
||||
win.add(view)
|
||||
view.show()
|
||||
win.show()
|
||||
|
||||
view.update()
|
||||
|
||||
def test_pickle_demo(self):
|
||||
import demo
|
||||
|
||||
canvas = demo.create_canvas()
|
||||
|
||||
pickled = pickle.dumps(canvas)
|
||||
|
||||
c2 = pickle.loads(pickled)
|
||||
|
||||
win = Gtk.Window()
|
||||
view = GtkView(canvas=c2)
|
||||
win.add(view)
|
||||
|
||||
view.show()
|
||||
win.show()
|
||||
|
||||
view.update()
|
||||
return cf
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
def test_pickle_element():
|
||||
item = Element()
|
||||
|
||||
# vim: sw=4:et:ai
|
||||
pickled = pickle.dumps(item)
|
||||
i2 = pickle.loads(pickled)
|
||||
|
||||
assert i2
|
||||
assert len(i2.handles()) == 4
|
||||
|
||||
|
||||
def test_pickle_line():
|
||||
item = Line()
|
||||
|
||||
pickled = pickle.dumps(item)
|
||||
i2 = pickle.loads(pickled)
|
||||
|
||||
assert i2
|
||||
assert len(i2.handles()) == 2
|
||||
|
||||
|
||||
def test_pickle(canvas_fixture):
|
||||
pickled = pickle.dumps(canvas_fixture.canvas)
|
||||
pickle.loads(pickled)
|
||||
|
||||
assert type(canvas_fixture.canvas._tree.nodes[0]) is Box
|
||||
assert type(canvas_fixture.canvas._tree.nodes[1]) is Box
|
||||
assert type(canvas_fixture.canvas._tree.nodes[2]) is Line
|
||||
|
||||
|
||||
def test_pickle_connect(canvas_fixture):
|
||||
"""Persist a connection.
|
||||
|
||||
"""
|
||||
canvas_fixture.line.handles()[0].visible = False
|
||||
canvas_fixture.line.handles()[0].connected_to = canvas_fixture.box
|
||||
canvas_fixture.line.handles()[0].disconnect = MyDisconnect()
|
||||
canvas_fixture.line.handles()[0].connection_data = 1
|
||||
|
||||
pickled = pickle.dumps(canvas_fixture.canvas)
|
||||
c2 = pickle.loads(pickled)
|
||||
|
||||
assert type(canvas_fixture.canvas._tree.nodes[0]) is Box
|
||||
assert type(canvas_fixture.canvas._tree.nodes[1]) is Box
|
||||
assert type(canvas_fixture.canvas._tree.nodes[2]) is Line
|
||||
assert c2.solver
|
||||
|
||||
line2 = c2._tree.nodes[2]
|
||||
h = line2.handles()[0]
|
||||
assert h.visible is False
|
||||
assert h.connected_to is c2._tree.nodes[0]
|
||||
|
||||
# Connection_data and disconnect have not been persisted
|
||||
assert h.connection_data == 1, h.connection_data
|
||||
assert h.disconnect, h.disconnect
|
||||
assert callable(h.disconnect)
|
||||
assert h.disconnect() is None, h.disconnect()
|
||||
|
||||
|
||||
def test_pickle_with_view(canvas_fixture):
|
||||
pickled = pickle.dumps(canvas_fixture.canvas)
|
||||
|
||||
c2 = pickle.loads(pickled)
|
||||
|
||||
view = View(canvas=c2)
|
||||
|
||||
surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, 0, 0)
|
||||
cr = cairo.Context(surface)
|
||||
view.update_bounding_box(cr)
|
||||
cr.show_page()
|
||||
surface.flush()
|
||||
surface.finish()
|
||||
|
||||
|
||||
def test_pickle_with_gtk_view(canvas_fixture):
|
||||
pickled = pickle.dumps(canvas_fixture.canvas)
|
||||
|
||||
c2 = pickle.loads(pickled)
|
||||
|
||||
win = Gtk.Window()
|
||||
view = GtkView(canvas=c2)
|
||||
win.add(view)
|
||||
|
||||
view.show()
|
||||
win.show()
|
||||
|
||||
view.update()
|
||||
|
||||
|
||||
def test_pickle_with_gtk_view_with_connection(canvas_fixture):
|
||||
box = canvas_fixture.canvas._tree.nodes[0]
|
||||
assert isinstance(box, Box)
|
||||
line = canvas_fixture.canvas._tree.nodes[2]
|
||||
assert isinstance(line, Line)
|
||||
|
||||
f = io.BytesIO()
|
||||
pickler = MyPickler(f)
|
||||
pickler.dump(canvas_fixture.canvas)
|
||||
pickled = f.getvalue()
|
||||
|
||||
c2 = pickle.loads(pickled)
|
||||
|
||||
win = Gtk.Window()
|
||||
view = GtkView(canvas=c2)
|
||||
win.add(view)
|
||||
view.show()
|
||||
win.show()
|
||||
|
||||
view.update()
|
||||
|
||||
|
||||
def test_pickle_demo():
|
||||
canvas = demo.create_canvas()
|
||||
|
||||
pickled = pickle.dumps(canvas)
|
||||
|
||||
c2 = pickle.loads(pickled)
|
||||
|
||||
win = Gtk.Window()
|
||||
view = GtkView(canvas=c2)
|
||||
win.add(view)
|
||||
|
||||
view.show()
|
||||
win.show()
|
||||
|
||||
view.update()
|
||||
|
@ -1,103 +1,91 @@
|
||||
from builtins import range
|
||||
import unittest
|
||||
|
||||
import pytest
|
||||
|
||||
from gaphas.geometry import Rectangle
|
||||
from gaphas.quadtree import Quadtree
|
||||
|
||||
|
||||
class QuadtreeTestCase(unittest.TestCase):
|
||||
def test_lookups(self):
|
||||
qtree = Quadtree((0, 0, 100, 100))
|
||||
for i in range(100, 10):
|
||||
for j in range(100, 10):
|
||||
qtree.add("%dx%d" % (i, j), (i, j, 10, 10))
|
||||
|
||||
for i in range(100, 10):
|
||||
for j in range(100, 10):
|
||||
assert qtree.find_intersect((i + 1, j + 1, 1, 1)) == [
|
||||
"%dx%d" % (i, j)
|
||||
], qtree.find_intersect((i + 1, j + 1, 1, 1))
|
||||
|
||||
def test_with_rectangles(self):
|
||||
from gaphas.geometry import Rectangle
|
||||
|
||||
qtree = Quadtree((0, 0, 100, 100))
|
||||
for i in range(0, 100, 10):
|
||||
for j in range(0, 100, 10):
|
||||
qtree.add("%dx%d" % (i, j), Rectangle(i, j, 10, 10))
|
||||
assert len(qtree._ids) == 100, len(qtree._ids)
|
||||
|
||||
for i in range(100, 10):
|
||||
for j in range(100, 10):
|
||||
assert qtree.find_intersect((i + 1, j + 1, 1, 1)) == [
|
||||
"%dx%d" % (i, j)
|
||||
], qtree.find_intersect((i + 1, j + 1, 1, 1))
|
||||
|
||||
def test_moving_items(self):
|
||||
qtree = Quadtree((0, 0, 100, 100), capacity=10)
|
||||
for i in range(0, 100, 10):
|
||||
for j in range(0, 100, 10):
|
||||
qtree.add("%dx%d" % (i, j), (i, j, 10, 10))
|
||||
assert len(qtree._ids) == 100, len(qtree._ids)
|
||||
assert qtree._bucket._buckets, qtree._bucket._buckets
|
||||
for i in range(4):
|
||||
assert qtree._bucket._buckets[i]._buckets
|
||||
for j in range(4):
|
||||
assert not qtree._bucket._buckets[i]._buckets[j]._buckets
|
||||
|
||||
# Check contents:
|
||||
# First sub-level contains 9 items. second level contains 4 items
|
||||
# ==> 4 * (9 + (4 * 4)) = 100
|
||||
assert len(qtree._bucket.items) == 0, qtree._bucket.items
|
||||
for i in range(4):
|
||||
assert len(qtree._bucket._buckets[i].items) == 9
|
||||
for item, bounds in qtree._bucket._buckets[i].items.items():
|
||||
assert qtree._bucket.find_bucket(bounds) is qtree._bucket._buckets[i]
|
||||
for j in range(4):
|
||||
assert len(qtree._bucket._buckets[i]._buckets[j].items) == 4
|
||||
|
||||
assert qtree.get_bounds("0x0")
|
||||
# Now move item '0x0' to the center of the first quadrant (20, 20)
|
||||
qtree.add("0x0", (20, 20, 10, 10))
|
||||
assert len(qtree._bucket.items) == 0
|
||||
assert len(qtree._bucket._buckets[0]._buckets[0].items) == 3, (
|
||||
qtree._bucket._buckets[0]._buckets[0].items
|
||||
)
|
||||
assert len(qtree._bucket._buckets[0].items) == 10, qtree._bucket._buckets[
|
||||
0
|
||||
].items
|
||||
|
||||
# Now move item '0x0' to the second quadrant (70, 20)
|
||||
qtree.add("0x0", (70, 20, 10, 10))
|
||||
assert len(qtree._bucket.items) == 0
|
||||
assert len(qtree._bucket._buckets[0]._buckets[0].items) == 3, (
|
||||
qtree._bucket._buckets[0]._buckets[0].items
|
||||
)
|
||||
assert len(qtree._bucket._buckets[0].items) == 9, qtree._bucket._buckets[
|
||||
0
|
||||
].items
|
||||
assert len(qtree._bucket._buckets[1].items) == 10, qtree._bucket._buckets[
|
||||
1
|
||||
].items
|
||||
|
||||
def test_get_data(self):
|
||||
"""
|
||||
Extra data may be added to a node:
|
||||
"""
|
||||
qtree = Quadtree((0, 0, 100, 100))
|
||||
for i in range(0, 100, 10):
|
||||
for j in range(0, 100, 10):
|
||||
qtree.add("%dx%d" % (i, j), (i, j, 10, 10), i + j)
|
||||
|
||||
for i in range(0, 100, 10):
|
||||
for j in range(0, 100, 10):
|
||||
assert i + j == qtree.get_data("%dx%d" % (i, j))
|
||||
|
||||
def test_clipped_bounds(self):
|
||||
qtree = Quadtree((0, 0, 100, 100), capacity=10)
|
||||
qtree.add(1, (-100, -100, 120, 120))
|
||||
self.assertEqual((0, 0, 20, 20), qtree.get_clipped_bounds(1))
|
||||
@pytest.fixture()
|
||||
def qtree():
|
||||
qtree = Quadtree((0, 0, 100, 100))
|
||||
for i in range(0, 100, 10):
|
||||
for j in range(0, 100, 10):
|
||||
qtree.add(item="%dx%d" % (i, j), bounds=Rectangle(i, j, 10, 10))
|
||||
return qtree
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
def test_lookups(qtree):
|
||||
for i in range(100, 10):
|
||||
for j in range(100, 10):
|
||||
assert qtree.find_intersect(rect=(i + 1, j + 1, 1, 1)) == [
|
||||
"%dx%d" % (i, j)
|
||||
], qtree.find_intersect(rect=(i + 1, j + 1, 1, 1))
|
||||
|
||||
# vim:sw=4:et:ai
|
||||
|
||||
def test_with_rectangles(qtree):
|
||||
assert len(qtree._ids) == 100, len(qtree._ids)
|
||||
|
||||
for i in range(100, 10):
|
||||
for j in range(100, 10):
|
||||
assert qtree.find_intersect(rect=(i + 1, j + 1, 1, 1)) == [
|
||||
"%dx%d" % (i, j)
|
||||
], qtree.find_intersect(rect=(i + 1, j + 1, 1, 1))
|
||||
|
||||
|
||||
def test_moving_items(qtree):
|
||||
qtree.capacity = 10
|
||||
assert len(qtree._ids) == 100, len(qtree._ids)
|
||||
assert qtree._bucket._buckets, qtree._bucket._buckets
|
||||
for i in range(4):
|
||||
assert qtree._bucket._buckets[i]._buckets
|
||||
for j in range(4):
|
||||
assert not qtree._bucket._buckets[i]._buckets[j]._buckets
|
||||
|
||||
# Check contents:
|
||||
# First sub-level contains 9 items. second level contains 4 items
|
||||
# ==> 4 * (9 + (4 * 4)) = 100
|
||||
assert len(qtree._bucket.items) == 0, qtree._bucket.items
|
||||
for i in range(4):
|
||||
assert len(qtree._bucket._buckets[i].items) == 9
|
||||
for item, bounds in qtree._bucket._buckets[i].items.items():
|
||||
assert qtree._bucket.find_bucket(bounds) is qtree._bucket._buckets[i]
|
||||
for j in range(4):
|
||||
assert len(qtree._bucket._buckets[i]._buckets[j].items) == 4
|
||||
|
||||
assert qtree.get_bounds("0x0")
|
||||
# Now move item '0x0' to the center of the first quadrant (20, 20)
|
||||
qtree.add("0x0", (20, 20, 10, 10))
|
||||
assert len(qtree._bucket.items) == 0
|
||||
assert len(qtree._bucket._buckets[0]._buckets[0].items) == 3, (
|
||||
qtree._bucket._buckets[0]._buckets[0].items
|
||||
)
|
||||
assert len(qtree._bucket._buckets[0].items) == 10, qtree._bucket._buckets[0].items
|
||||
|
||||
# Now move item '0x0' to the second quadrant (70, 20)
|
||||
qtree.add("0x0", (70, 20, 10, 10))
|
||||
assert len(qtree._bucket.items) == 0
|
||||
assert len(qtree._bucket._buckets[0]._buckets[0].items) == 3, (
|
||||
qtree._bucket._buckets[0]._buckets[0].items
|
||||
)
|
||||
assert len(qtree._bucket._buckets[0].items) == 9, qtree._bucket._buckets[0].items
|
||||
assert len(qtree._bucket._buckets[1].items) == 10, qtree._bucket._buckets[1].items
|
||||
|
||||
|
||||
def test_get_data(qtree):
|
||||
"""Test extra data added to a node.
|
||||
|
||||
"""
|
||||
for i in range(0, 100, 10):
|
||||
for j in range(0, 100, 10):
|
||||
qtree.add(item="%dx%d" % (i, j), bounds=(i, j, 10, 10), data=i + j)
|
||||
|
||||
for i in range(0, 100, 10):
|
||||
for j in range(0, 100, 10):
|
||||
assert i + j == qtree.get_data(item="%dx%d" % (i, j))
|
||||
|
||||
|
||||
def test_clipped_bounds(qtree):
|
||||
qtree.capacity = 10
|
||||
qtree.add(item=1, bounds=(-100, -100, 120, 120))
|
||||
assert (0, 0, 20, 20) == qtree.get_clipped_bounds(item=1)
|
||||
|
@ -1,479 +1,418 @@
|
||||
"""
|
||||
Generic gaphas item tests.
|
||||
"""Test segment aspects for items.
|
||||
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
import unittest
|
||||
import pytest
|
||||
|
||||
from gaphas.item import Item, Line
|
||||
from gaphas.segment import *
|
||||
from gaphas.canvas import Canvas
|
||||
from gaphas.item import Item
|
||||
from gaphas.segment import *
|
||||
from gaphas.view import View
|
||||
from gaphas import state
|
||||
from gaphas.tests.test_tool import simple_canvas
|
||||
|
||||
|
||||
class SegmentTestCase(unittest.TestCase):
|
||||
"""
|
||||
Test aspects for items.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
self.canvas = Canvas()
|
||||
self.line = Line()
|
||||
self.view = View(self.canvas)
|
||||
|
||||
def test_segment_fails_for_item(self):
|
||||
"""
|
||||
Test if Segment aspect can be applied to Item
|
||||
"""
|
||||
item = Item()
|
||||
try:
|
||||
s = Segment(item, self.view)
|
||||
except TypeError as e:
|
||||
pass
|
||||
else:
|
||||
assert False, "Should not be reached"
|
||||
|
||||
def test_segment(self):
|
||||
"""
|
||||
"""
|
||||
view = self.view
|
||||
line = Line()
|
||||
self.canvas.add(line)
|
||||
segment = Segment(line, self.view)
|
||||
self.assertEqual(2, len(line.handles()))
|
||||
segment.split((5, 5))
|
||||
self.assertEqual(3, len(line.handles()))
|
||||
|
||||
|
||||
undo_list = []
|
||||
redo_list = []
|
||||
|
||||
|
||||
def undo_handler(event):
|
||||
undo_list.append(event)
|
||||
|
||||
|
||||
def undo():
|
||||
apply_me = list(undo_list)
|
||||
del undo_list[:]
|
||||
apply_me.reverse()
|
||||
for e in apply_me:
|
||||
state.saveapply(*e)
|
||||
redo_list[:] = undo_list[:]
|
||||
del undo_list[:]
|
||||
|
||||
|
||||
class TestCaseBase(unittest.TestCase):
|
||||
"""
|
||||
Abstract test case class with undo support.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
state.observers.add(state.revert_handler)
|
||||
state.subscribers.add(undo_handler)
|
||||
simple_canvas(self)
|
||||
|
||||
def tearDown(self):
|
||||
state.observers.remove(state.revert_handler)
|
||||
state.subscribers.remove(undo_handler)
|
||||
|
||||
|
||||
class LineSplitTestCase(TestCaseBase):
|
||||
"""
|
||||
Tests for line splitting.
|
||||
"""
|
||||
|
||||
def test_split_single(self):
|
||||
"""Test single line splitting
|
||||
"""
|
||||
# we start with two handles and one port, after split 3 handles are
|
||||
# expected and 2 ports
|
||||
assert len(self.line.handles()) == 2
|
||||
assert len(self.line.ports()) == 1
|
||||
|
||||
old_port = self.line.ports()[0]
|
||||
h1, h2 = self.line.handles()
|
||||
self.assertEqual(h1.pos, old_port.start)
|
||||
self.assertEqual(h2.pos, old_port.end)
|
||||
|
||||
segment = Segment(self.line, self.view)
|
||||
|
||||
handles, ports = segment.split_segment(0)
|
||||
handle = handles[0]
|
||||
self.assertEqual(1, len(handles))
|
||||
self.assertEqual((50, 50), handle.pos.pos)
|
||||
self.assertEqual(3, len(self.line.handles()))
|
||||
self.assertEqual(2, len(self.line.ports()))
|
||||
|
||||
# new handle is between old handles
|
||||
self.assertEqual(handle, self.line.handles()[1])
|
||||
# and old port is deleted
|
||||
self.assertTrue(old_port not in self.line.ports())
|
||||
|
||||
# check ports order
|
||||
p1, p2 = self.line.ports()
|
||||
h1, h2, h3 = self.line.handles()
|
||||
self.assertEqual(h1.pos, p1.start)
|
||||
self.assertEqual(h2.pos, p1.end)
|
||||
self.assertEqual(h2.pos, p2.start)
|
||||
self.assertEqual(h3.pos, p2.end)
|
||||
|
||||
def test_split_multiple(self):
|
||||
"""Test multiple line splitting
|
||||
"""
|
||||
self.line.handles()[1].pos = (20, 16)
|
||||
handles = self.line.handles()
|
||||
old_ports = self.line.ports()[:]
|
||||
|
||||
# start with two handles, split into 4 segments - 3 new handles to
|
||||
# be expected
|
||||
assert len(handles) == 2
|
||||
assert len(old_ports) == 1
|
||||
|
||||
segment = Segment(self.line, self.view)
|
||||
|
||||
handles, ports = segment.split_segment(0, count=4)
|
||||
self.assertEqual(3, len(handles))
|
||||
h1, h2, h3 = handles
|
||||
self.assertEqual((5, 4), h1.pos.pos)
|
||||
self.assertEqual((10, 8), h2.pos.pos)
|
||||
self.assertEqual((15, 12), h3.pos.pos)
|
||||
|
||||
# new handles between old handles
|
||||
self.assertEqual(5, len(self.line.handles()))
|
||||
self.assertEqual(h1, self.line.handles()[1])
|
||||
self.assertEqual(h2, self.line.handles()[2])
|
||||
self.assertEqual(h3, self.line.handles()[3])
|
||||
|
||||
self.assertEqual(4, len(self.line.ports()))
|
||||
|
||||
# and old port is deleted
|
||||
self.assertTrue(old_ports[0] not in self.line.ports())
|
||||
|
||||
# check ports order
|
||||
p1, p2, p3, p4 = self.line.ports()
|
||||
h1, h2, h3, h4, h5 = self.line.handles()
|
||||
self.assertEqual(h1.pos, p1.start)
|
||||
self.assertEqual(h2.pos, p1.end)
|
||||
self.assertEqual(h2.pos, p2.start)
|
||||
self.assertEqual(h3.pos, p2.end)
|
||||
self.assertEqual(h3.pos, p3.start)
|
||||
self.assertEqual(h4.pos, p3.end)
|
||||
self.assertEqual(h4.pos, p4.start)
|
||||
self.assertEqual(h5.pos, p4.end)
|
||||
|
||||
def test_ports_after_split(self):
|
||||
"""Test ports removal after split
|
||||
"""
|
||||
self.line.handles()[1].pos = (20, 16)
|
||||
|
||||
segment = Segment(self.line, self.view)
|
||||
|
||||
segment.split_segment(0)
|
||||
handles = self.line.handles()
|
||||
old_ports = self.line.ports()[:]
|
||||
|
||||
# start with 3 handles and two ports
|
||||
assert len(handles) == 3
|
||||
assert len(old_ports) == 2
|
||||
|
||||
# do split of first segment again
|
||||
# first port should be deleted, but 2nd one should remain untouched
|
||||
segment.split_segment(0)
|
||||
self.assertFalse(old_ports[0] in self.line.ports())
|
||||
self.assertEqual(old_ports[1], self.line.ports()[2])
|
||||
|
||||
def test_constraints_after_split(self):
|
||||
"""Test if constraints are recreated after line split
|
||||
"""
|
||||
|
||||
# connect line2 to self.line
|
||||
line2 = Line()
|
||||
self.canvas.add(line2)
|
||||
head = line2.handles()[0]
|
||||
self.tool.connect(line2, head, (25, 25))
|
||||
cinfo = self.canvas.get_connection(head)
|
||||
self.assertEqual(self.line, cinfo.connected)
|
||||
|
||||
Segment(self.line, self.view).split_segment(0)
|
||||
assert len(self.line.handles()) == 3
|
||||
h1, h2, h3 = self.line.handles()
|
||||
|
||||
cinfo = self.canvas.get_connection(head)
|
||||
# connection shall be reconstrained between 1st and 2nd handle
|
||||
self.assertEqual(h1.pos, cinfo.constraint._line[0]._point)
|
||||
self.assertEqual(h2.pos, cinfo.constraint._line[1]._point)
|
||||
|
||||
def test_split_undo(self):
|
||||
"""Test line splitting undo
|
||||
"""
|
||||
self.line.handles()[1].pos = (20, 0)
|
||||
|
||||
# we start with two handles and one port, after split 3 handles and
|
||||
# 2 ports are expected
|
||||
assert len(self.line.handles()) == 2
|
||||
assert len(self.line.ports()) == 1
|
||||
|
||||
segment = Segment(self.line, self.view)
|
||||
segment.split_segment(0)
|
||||
assert len(self.line.handles()) == 3
|
||||
assert len(self.line.ports()) == 2
|
||||
|
||||
# after undo, 2 handles and 1 port are expected again
|
||||
undo()
|
||||
self.assertEqual(2, len(self.line.handles()))
|
||||
self.assertEqual(1, len(self.line.ports()))
|
||||
|
||||
def test_orthogonal_line_split(self):
|
||||
"""Test orthogonal line splitting
|
||||
"""
|
||||
# start with no orthogonal constraints
|
||||
assert len(self.line._orthogonal_constraints) == 0
|
||||
|
||||
segment = Segment(self.line, None)
|
||||
segment.split_segment(0)
|
||||
|
||||
self.line.orthogonal = True
|
||||
|
||||
# check orthogonal constraints
|
||||
self.assertEqual(2, len(self.line._orthogonal_constraints))
|
||||
self.assertEqual(3, len(self.line.handles()))
|
||||
|
||||
Segment(self.line, self.view).split_segment(0)
|
||||
|
||||
# 3 handles and 2 ports are expected
|
||||
# 2 constraints keep the self.line orthogonal
|
||||
self.assertEqual(3, len(self.line._orthogonal_constraints))
|
||||
self.assertEqual(4, len(self.line.handles()))
|
||||
self.assertEqual(3, len(self.line.ports()))
|
||||
|
||||
def test_params_errors(self):
|
||||
"""Test parameter error exceptions
|
||||
"""
|
||||
line = Line()
|
||||
segment = Segment(line, self.view)
|
||||
|
||||
# there is only 1 segment
|
||||
self.assertRaises(ValueError, segment.split_segment, -1)
|
||||
|
||||
line = Line()
|
||||
segment = Segment(line, self.view)
|
||||
self.assertRaises(ValueError, segment.split_segment, 1)
|
||||
|
||||
line = Line()
|
||||
# can't split into one or less segment :)
|
||||
segment = Segment(line, self.view)
|
||||
self.assertRaises(ValueError, segment.split_segment, 0, 1)
|
||||
|
||||
|
||||
class LineMergeTestCase(TestCaseBase):
|
||||
"""
|
||||
Tests for line merging.
|
||||
"""
|
||||
|
||||
def test_merge_first_single(self):
|
||||
"""Test single line merging starting from 1st segment
|
||||
"""
|
||||
self.line.handles()[1].pos = (20, 0)
|
||||
segment = Segment(self.line, self.view)
|
||||
segment.split_segment(0)
|
||||
|
||||
# we start with 3 handles and 2 ports, after merging 2 handles and
|
||||
# 1 port are expected
|
||||
assert len(self.line.handles()) == 3
|
||||
assert len(self.line.ports()) == 2
|
||||
old_ports = self.line.ports()[:]
|
||||
|
||||
segment = Segment(self.line, self.view)
|
||||
handles, ports = segment.merge_segment(0)
|
||||
# deleted handles and ports
|
||||
self.assertEqual(1, len(handles))
|
||||
self.assertEqual(2, len(ports))
|
||||
# handles and ports left after segment merging
|
||||
self.assertEqual(2, len(self.line.handles()))
|
||||
self.assertEqual(1, len(self.line.ports()))
|
||||
|
||||
self.assertTrue(handles[0] not in self.line.handles())
|
||||
self.assertTrue(ports[0] not in self.line.ports())
|
||||
self.assertTrue(ports[1] not in self.line.ports())
|
||||
|
||||
# old ports are completely removed as they are replaced by new one
|
||||
# port
|
||||
self.assertEqual(old_ports, ports)
|
||||
|
||||
# finally, created port shall span between first and last handle
|
||||
port = self.line.ports()[0]
|
||||
self.assertEqual((0, 0), port.start.pos)
|
||||
self.assertEqual((20, 0), port.end.pos)
|
||||
|
||||
def test_constraints_after_merge(self):
|
||||
"""Test if constraints are recreated after line merge
|
||||
"""
|
||||
|
||||
# connect line2 to self.line
|
||||
line2 = Line()
|
||||
self.canvas.add(line2)
|
||||
head = line2.handles()[0]
|
||||
|
||||
# conn = Connector(line2, head)
|
||||
# sink = conn.glue((25, 25))
|
||||
# assert sink is not None
|
||||
|
||||
# conn.connect(sink)
|
||||
|
||||
self.tool.connect(line2, head, (25, 25))
|
||||
cinfo = self.canvas.get_connection(head)
|
||||
self.assertEqual(self.line, cinfo.connected)
|
||||
|
||||
segment = Segment(self.line, self.view)
|
||||
segment.split_segment(0)
|
||||
assert len(self.line.handles()) == 3
|
||||
c1 = cinfo.constraint
|
||||
|
||||
segment.merge_segment(0)
|
||||
assert len(self.line.handles()) == 2
|
||||
|
||||
h1, h2 = self.line.handles()
|
||||
# connection shall be reconstrained between 1st and 2nd handle
|
||||
cinfo = self.canvas.get_connection(head)
|
||||
self.assertEqual(cinfo.constraint._line[0]._point, h1.pos)
|
||||
self.assertEqual(cinfo.constraint._line[1]._point, h2.pos)
|
||||
self.assertFalse(c1 == cinfo.constraint)
|
||||
|
||||
def test_merge_multiple(self):
|
||||
"""Test multiple line merge
|
||||
"""
|
||||
self.line.handles()[1].pos = (20, 16)
|
||||
segment = Segment(self.line, self.view)
|
||||
segment.split_segment(0, count=3)
|
||||
|
||||
# start with 4 handles and 3 ports, merge 3 segments
|
||||
assert len(self.line.handles()) == 4
|
||||
assert len(self.line.ports()) == 3
|
||||
|
||||
handles, ports = segment.merge_segment(0, count=3)
|
||||
self.assertEqual(2, len(handles))
|
||||
self.assertEqual(3, len(ports))
|
||||
self.assertEqual(2, len(self.line.handles()))
|
||||
self.assertEqual(1, len(self.line.ports()))
|
||||
|
||||
self.assertTrue(not set(handles).intersection(set(self.line.handles())))
|
||||
self.assertTrue(not set(ports).intersection(set(self.line.ports())))
|
||||
|
||||
# finally, created port shall span between first and last handle
|
||||
port = self.line.ports()[0]
|
||||
self.assertEqual((0, 0), port.start.pos)
|
||||
self.assertEqual((20, 16), port.end.pos)
|
||||
|
||||
def test_merge_undo(self):
|
||||
"""Test line merging undo
|
||||
"""
|
||||
self.line.handles()[1].pos = (20, 0)
|
||||
|
||||
segment = Segment(self.line, self.view)
|
||||
|
||||
# split for merging
|
||||
segment.split_segment(0)
|
||||
assert len(self.line.handles()) == 3
|
||||
assert len(self.line.ports()) == 2
|
||||
|
||||
# clear undo stack before merging
|
||||
del undo_list[:]
|
||||
|
||||
# merge with empty undo stack
|
||||
segment.merge_segment(0)
|
||||
assert len(self.line.handles()) == 2
|
||||
assert len(self.line.ports()) == 1
|
||||
|
||||
# after merge undo, 3 handles and 2 ports are expected again
|
||||
undo()
|
||||
self.assertEqual(3, len(self.line.handles()))
|
||||
self.assertEqual(2, len(self.line.ports()))
|
||||
|
||||
def test_orthogonal_line_merge(self):
|
||||
"""Test orthogonal line merging
|
||||
"""
|
||||
self.assertEqual(4, len(self.canvas.solver._constraints))
|
||||
|
||||
self.line.handles()[-1].pos = 100, 100
|
||||
|
||||
segment = Segment(self.line, self.view)
|
||||
# prepare the self.line for merging
|
||||
segment.split_segment(0)
|
||||
segment.split_segment(0)
|
||||
self.line.orthogonal = True
|
||||
|
||||
self.assertEqual(4 + 3, len(self.canvas.solver._constraints))
|
||||
self.assertEqual(4, len(self.line.handles()))
|
||||
self.assertEqual(3, len(self.line.ports()))
|
||||
|
||||
# test the merging
|
||||
segment.merge_segment(0)
|
||||
|
||||
self.assertEqual(4 + 2, len(self.canvas.solver._constraints))
|
||||
self.assertEqual(3, len(self.line.handles()))
|
||||
self.assertEqual(2, len(self.line.ports()))
|
||||
|
||||
def test_params_errors(self):
|
||||
"""Test parameter error exceptions
|
||||
"""
|
||||
line = Line()
|
||||
self.canvas.add(line)
|
||||
segment = Segment(line, self.view)
|
||||
segment.split_segment(0)
|
||||
# no segment -1
|
||||
self.assertRaises(ValueError, segment.merge_segment, -1)
|
||||
|
||||
line = Line()
|
||||
self.canvas.add(line)
|
||||
segment = Segment(line, self.view)
|
||||
segment.split_segment(0)
|
||||
# no segment no 2
|
||||
self.assertRaises(ValueError, segment.merge_segment, 2)
|
||||
|
||||
line = Line()
|
||||
self.canvas.add(line)
|
||||
segment = Segment(line, self.view)
|
||||
segment.split_segment(0)
|
||||
# can't merge one or less segments :)
|
||||
self.assertRaises(ValueError, segment.merge_segment, 0, 1)
|
||||
|
||||
line = Line()
|
||||
self.canvas.add(line)
|
||||
self.assertEqual(2, len(line.handles()))
|
||||
segment = Segment(line, self.view)
|
||||
# can't merge line with one segment
|
||||
self.assertRaises(ValueError, segment.merge_segment, 0)
|
||||
|
||||
line = Line()
|
||||
self.canvas.add(line)
|
||||
segment = Segment(line, self.view)
|
||||
segment.split_segment(0)
|
||||
# 2 segments: no 0 and 1. cannot merge as there are no segments
|
||||
# after segment no 1
|
||||
self.assertRaises(ValueError, segment.merge_segment, 1)
|
||||
|
||||
line = Line()
|
||||
self.canvas.add(line)
|
||||
segment = Segment(line, self.view)
|
||||
segment.split_segment(0)
|
||||
# 2 segments: no 0 and 1. cannot merge 3 segments as there are no 3
|
||||
# segments
|
||||
self.assertRaises(ValueError, segment.merge_segment, 0, 3)
|
||||
|
||||
|
||||
class SegmentHandlesTest(unittest.TestCase):
|
||||
def setUp(self):
|
||||
class SegmentFixture(object):
|
||||
def __init__(self):
|
||||
self.canvas = Canvas()
|
||||
self.line = Line()
|
||||
self.canvas.add(self.line)
|
||||
self.view = View(self.canvas)
|
||||
|
||||
def testHandleFinder(self):
|
||||
finder = HandleFinder(self.line, self.view)
|
||||
assert type(finder) is SegmentHandleFinder, type(finder)
|
||||
self.item = Item()
|
||||
|
||||
|
||||
# vim:sw=4:et:ai
|
||||
@pytest.fixture(name="seg")
|
||||
def segment_fixture():
|
||||
return SegmentFixture()
|
||||
|
||||
|
||||
def test_segment_fails_for_item(seg):
|
||||
"""Test if Segment aspect can be applied to Item.
|
||||
|
||||
"""
|
||||
try:
|
||||
s = Segment(seg.item, seg.view)
|
||||
except TypeError as e:
|
||||
pass
|
||||
else:
|
||||
assert False, "Should not be reached"
|
||||
|
||||
|
||||
def test_segment(seg):
|
||||
"""Test add a new segment to a line.
|
||||
|
||||
"""
|
||||
line = Line()
|
||||
seg.canvas.add(line)
|
||||
segment = Segment(line, seg.view)
|
||||
assert 2 == len(line.handles())
|
||||
segment.split((5, 5))
|
||||
assert 3 == len(line.handles())
|
||||
|
||||
|
||||
# Test Line Splitting
|
||||
|
||||
|
||||
def test_split_single(simple_canvas):
|
||||
"""Test single line splitting
|
||||
"""
|
||||
# Start with 2 handles & 1 port, after split: expect 3 handles & 2 ports
|
||||
assert len(simple_canvas.line.handles()) == 2
|
||||
assert len(simple_canvas.line.ports()) == 1
|
||||
|
||||
old_port = simple_canvas.line.ports()[0]
|
||||
h1, h2 = simple_canvas.line.handles()
|
||||
assert h1.pos == old_port.start
|
||||
assert h2.pos == old_port.end
|
||||
|
||||
segment = Segment(simple_canvas.line, simple_canvas.view)
|
||||
|
||||
handles, ports = segment.split_segment(0)
|
||||
handle = handles[0]
|
||||
assert 1 == len(handles)
|
||||
assert (50, 50) == handle.pos.pos
|
||||
assert 3 == len(simple_canvas.line.handles())
|
||||
assert 2 == len(simple_canvas.line.ports())
|
||||
|
||||
# New handle is between old handles
|
||||
assert handle == simple_canvas.line.handles()[1]
|
||||
# The old port is deleted
|
||||
assert old_port not in simple_canvas.line.ports()
|
||||
|
||||
# check ports order
|
||||
p1, p2 = simple_canvas.line.ports()
|
||||
h1, h2, h3 = simple_canvas.line.handles()
|
||||
assert h1.pos == p1.start
|
||||
assert h2.pos == p1.end
|
||||
assert h2.pos == p2.start
|
||||
assert h3.pos == p2.end
|
||||
|
||||
|
||||
def test_split_multiple(simple_canvas):
|
||||
"""Test multiple line splitting.
|
||||
|
||||
"""
|
||||
simple_canvas.line.handles()[1].pos = (20, 16)
|
||||
handles = simple_canvas.line.handles()
|
||||
old_ports = simple_canvas.line.ports()[:]
|
||||
|
||||
# Start with two handles, split into 4 segments: expect 3 new handles
|
||||
assert len(handles) == 2
|
||||
assert len(old_ports) == 1
|
||||
|
||||
segment = Segment(simple_canvas.line, simple_canvas.view)
|
||||
|
||||
handles, ports = segment.split_segment(0, count=4)
|
||||
assert 3 == len(handles)
|
||||
h1, h2, h3 = handles
|
||||
assert (5, 4) == h1.pos.pos
|
||||
assert (10, 8) == h2.pos.pos
|
||||
assert (15, 12) == h3.pos.pos
|
||||
|
||||
# New handles between old handles
|
||||
assert 5 == len(simple_canvas.line.handles())
|
||||
assert h1 == simple_canvas.line.handles()[1]
|
||||
assert h2 == simple_canvas.line.handles()[2]
|
||||
assert h3 == simple_canvas.line.handles()[3]
|
||||
|
||||
assert 4 == len(simple_canvas.line.ports())
|
||||
|
||||
# The old port is deleted
|
||||
assert old_ports[0] not in simple_canvas.line.ports()
|
||||
|
||||
# Check ports order
|
||||
p1, p2, p3, p4 = simple_canvas.line.ports()
|
||||
h1, h2, h3, h4, h5 = simple_canvas.line.handles()
|
||||
assert h1.pos == p1.start
|
||||
assert h2.pos == p1.end
|
||||
assert h2.pos == p2.start
|
||||
assert h3.pos == p2.end
|
||||
assert h3.pos == p3.start
|
||||
assert h4.pos == p3.end
|
||||
assert h4.pos == p4.start
|
||||
assert h5.pos == p4.end
|
||||
|
||||
|
||||
def test_ports_after_split(simple_canvas):
|
||||
"""Test ports removal after split
|
||||
"""
|
||||
simple_canvas.line.handles()[1].pos = (20, 16)
|
||||
|
||||
segment = Segment(simple_canvas.line, simple_canvas.view)
|
||||
|
||||
segment.split_segment(0)
|
||||
handles = simple_canvas.line.handles()
|
||||
old_ports = simple_canvas.line.ports()[:]
|
||||
|
||||
# Start with 3 handles and 2 ports
|
||||
assert len(handles) == 3
|
||||
assert len(old_ports) == 2
|
||||
|
||||
# Split 1st segment again: 1st port should be deleted, but 2nd one should
|
||||
# remain untouched
|
||||
segment.split_segment(0)
|
||||
assert not (old_ports[0] in simple_canvas.line.ports())
|
||||
assert old_ports[1] == simple_canvas.line.ports()[2]
|
||||
|
||||
|
||||
def test_constraints_after_split(simple_canvas):
|
||||
"""Test if constraints are recreated after line split.
|
||||
|
||||
"""
|
||||
# Connect line2 to self.line
|
||||
line2 = Line()
|
||||
simple_canvas.canvas.add(line2)
|
||||
head = line2.handles()[0]
|
||||
simple_canvas.tool.connect(line2, head, (25, 25))
|
||||
cinfo = simple_canvas.canvas.get_connection(head)
|
||||
assert simple_canvas.line == cinfo.connected
|
||||
|
||||
Segment(simple_canvas.line, simple_canvas.view).split_segment(0)
|
||||
assert len(simple_canvas.line.handles()) == 3
|
||||
h1, h2, h3 = simple_canvas.line.handles()
|
||||
|
||||
cinfo = simple_canvas.canvas.get_connection(head)
|
||||
# Connection shall be reconstrained between 1st and 2nd handle
|
||||
assert h1.pos == cinfo.constraint._line[0]._point
|
||||
assert h2.pos == cinfo.constraint._line[1]._point
|
||||
|
||||
|
||||
def test_split_undo(simple_canvas, revert_undo, undo_fixture):
|
||||
"""Test line splitting undo.
|
||||
|
||||
"""
|
||||
simple_canvas.line.handles()[1].pos = (20, 0)
|
||||
|
||||
# We start with two handles and one port, after split 3 handles and
|
||||
# 2 ports are expected
|
||||
assert len(simple_canvas.line.handles()) == 2
|
||||
assert len(simple_canvas.line.ports()) == 1
|
||||
|
||||
segment = Segment(simple_canvas.line, simple_canvas.view)
|
||||
segment.split_segment(0)
|
||||
assert len(simple_canvas.line.handles()) == 3
|
||||
assert len(simple_canvas.line.ports()) == 2
|
||||
|
||||
# After undo, 2 handles and 1 port are expected again
|
||||
undo_fixture[0]() # Call Undo
|
||||
assert 2 == len(simple_canvas.line.handles())
|
||||
assert 1 == len(simple_canvas.line.ports())
|
||||
|
||||
|
||||
def test_orthogonal_line_split(simple_canvas):
|
||||
"""Test orthogonal line splitting.
|
||||
|
||||
"""
|
||||
# Start with no orthogonal constraints
|
||||
assert len(simple_canvas.line._orthogonal_constraints) == 0
|
||||
|
||||
segment = Segment(simple_canvas.line, None)
|
||||
segment.split_segment(0)
|
||||
|
||||
simple_canvas.line.orthogonal = True
|
||||
|
||||
# Check orthogonal constraints
|
||||
assert 2 == len(simple_canvas.line._orthogonal_constraints)
|
||||
assert 3 == len(simple_canvas.line.handles())
|
||||
|
||||
Segment(simple_canvas.line, simple_canvas.view).split_segment(0)
|
||||
|
||||
# 3 handles and 2 ports are expected
|
||||
# 2 constraints keep the self.line orthogonal
|
||||
assert 3 == len(simple_canvas.line._orthogonal_constraints)
|
||||
assert 4 == len(simple_canvas.line.handles())
|
||||
assert 3 == len(simple_canvas.line.ports())
|
||||
|
||||
|
||||
def test_params_error_exc(simple_canvas):
|
||||
"""Test parameter error exceptions.
|
||||
|
||||
"""
|
||||
line = Line()
|
||||
segment = Segment(line, simple_canvas.view)
|
||||
|
||||
# There is only 1 segment
|
||||
with pytest.raises(ValueError):
|
||||
segment.split_segment(-1)
|
||||
|
||||
line = Line()
|
||||
segment = Segment(line, simple_canvas.view)
|
||||
with pytest.raises(ValueError):
|
||||
segment.split_segment(1)
|
||||
|
||||
line = Line()
|
||||
# Can't split into one or less segment :)
|
||||
segment = Segment(line, simple_canvas.view)
|
||||
with pytest.raises(ValueError):
|
||||
segment.split_segment(0, 1)
|
||||
|
||||
|
||||
# Test Line Merging
|
||||
|
||||
|
||||
def test_merge_first_single(simple_canvas):
|
||||
"""Test single line merging starting from 1st segment.
|
||||
|
||||
"""
|
||||
simple_canvas.line.handles()[1].pos = (20, 0)
|
||||
segment = Segment(simple_canvas.line, simple_canvas.view)
|
||||
segment.split_segment(0)
|
||||
|
||||
# We start with 3 handles and 2 ports, after merging 2 handles and 1 port
|
||||
# are expected
|
||||
assert len(simple_canvas.line.handles()) == 3
|
||||
assert len(simple_canvas.line.ports()) == 2
|
||||
old_ports = simple_canvas.line.ports()[:]
|
||||
|
||||
segment = Segment(simple_canvas.line, simple_canvas.view)
|
||||
handles, ports = segment.merge_segment(0)
|
||||
# Deleted handles and ports
|
||||
assert 1 == len(handles)
|
||||
assert 2 == len(ports)
|
||||
# Handles and ports left after segment merging
|
||||
assert 2 == len(simple_canvas.line.handles())
|
||||
assert 1 == len(simple_canvas.line.ports())
|
||||
|
||||
assert handles[0] not in simple_canvas.line.handles()
|
||||
assert ports[0] not in simple_canvas.line.ports()
|
||||
assert ports[1] not in simple_canvas.line.ports()
|
||||
|
||||
# Old ports are completely removed as they are replaced by new one port
|
||||
assert old_ports == ports
|
||||
|
||||
# Finally, created port shall span between first and last handle
|
||||
port = simple_canvas.line.ports()[0]
|
||||
assert (0, 0) == port.start.pos
|
||||
assert (20, 0) == port.end.pos
|
||||
|
||||
|
||||
def test_constraints_after_merge(simple_canvas):
|
||||
"""Test if constraints are recreated after line merge.
|
||||
|
||||
"""
|
||||
line2 = Line()
|
||||
simple_canvas.canvas.add(line2)
|
||||
head = line2.handles()[0]
|
||||
|
||||
simple_canvas.tool.connect(line2, head, (25, 25))
|
||||
cinfo = simple_canvas.canvas.get_connection(head)
|
||||
assert simple_canvas.line == cinfo.connected
|
||||
|
||||
segment = Segment(simple_canvas.line, simple_canvas.view)
|
||||
segment.split_segment(0)
|
||||
assert len(simple_canvas.line.handles()) == 3
|
||||
c1 = cinfo.constraint
|
||||
|
||||
segment.merge_segment(0)
|
||||
assert len(simple_canvas.line.handles()) == 2
|
||||
|
||||
h1, h2 = simple_canvas.line.handles()
|
||||
# Connection shall be reconstrained between 1st and 2nd handle
|
||||
cinfo = simple_canvas.canvas.get_connection(head)
|
||||
assert cinfo.constraint._line[0]._point == h1.pos
|
||||
assert cinfo.constraint._line[1]._point == h2.pos
|
||||
assert not (c1 == cinfo.constraint)
|
||||
|
||||
|
||||
def test_merge_multiple(simple_canvas):
|
||||
"""Test multiple line merge.
|
||||
|
||||
"""
|
||||
simple_canvas.line.handles()[1].pos = (20, 16)
|
||||
segment = Segment(simple_canvas.line, simple_canvas.view)
|
||||
segment.split_segment(0, count=3)
|
||||
|
||||
# Start with 4 handles and 3 ports, merge 3 segments
|
||||
assert len(simple_canvas.line.handles()) == 4
|
||||
assert len(simple_canvas.line.ports()) == 3
|
||||
|
||||
handles, ports = segment.merge_segment(0, count=3)
|
||||
assert 2 == len(handles)
|
||||
assert 3 == len(ports)
|
||||
assert 2 == len(simple_canvas.line.handles())
|
||||
assert 1 == len(simple_canvas.line.ports())
|
||||
|
||||
assert not set(handles).intersection(set(simple_canvas.line.handles()))
|
||||
assert not set(ports).intersection(set(simple_canvas.line.ports()))
|
||||
|
||||
# Finally, the created port shall span between first and last handle
|
||||
port = simple_canvas.line.ports()[0]
|
||||
assert (0, 0) == port.start.pos
|
||||
assert (20, 16) == port.end.pos
|
||||
|
||||
|
||||
def test_merge_undo(simple_canvas, revert_undo, undo_fixture):
|
||||
"""Test line merging undo.
|
||||
|
||||
"""
|
||||
simple_canvas.line.handles()[1].pos = (20, 0)
|
||||
|
||||
segment = Segment(simple_canvas.line, simple_canvas.view)
|
||||
|
||||
# Split for merging
|
||||
segment.split_segment(0)
|
||||
assert len(simple_canvas.line.handles()) == 3
|
||||
assert len(simple_canvas.line.ports()) == 2
|
||||
|
||||
# Clear undo stack before merging
|
||||
del undo_fixture[2][:]
|
||||
|
||||
# Merge with empty undo stack
|
||||
segment.merge_segment(0)
|
||||
assert len(simple_canvas.line.handles()) == 2
|
||||
assert len(simple_canvas.line.ports()) == 1
|
||||
|
||||
# After merge undo, 3 handles and 2 ports are expected again
|
||||
undo_fixture[0]() # Undo
|
||||
assert 3 == len(simple_canvas.line.handles())
|
||||
assert 2 == len(simple_canvas.line.ports())
|
||||
|
||||
|
||||
def test_orthogonal_line_merge(simple_canvas):
|
||||
"""Test orthogonal line merging.
|
||||
|
||||
"""
|
||||
assert 4 == len(simple_canvas.canvas.solver._constraints)
|
||||
|
||||
simple_canvas.line.handles()[-1].pos = 100, 100
|
||||
|
||||
segment = Segment(simple_canvas.line, simple_canvas.view)
|
||||
# Prepare the self.line for merging
|
||||
segment.split_segment(0)
|
||||
segment.split_segment(0)
|
||||
simple_canvas.line.orthogonal = True
|
||||
|
||||
assert 4 + 3 == len(simple_canvas.canvas.solver._constraints)
|
||||
assert 4 == len(simple_canvas.line.handles())
|
||||
assert 3 == len(simple_canvas.line.ports())
|
||||
|
||||
# Test the merging
|
||||
segment.merge_segment(0)
|
||||
|
||||
assert 4 + 2 == len(simple_canvas.canvas.solver._constraints)
|
||||
assert 3 == len(simple_canvas.line.handles())
|
||||
assert 2 == len(simple_canvas.line.ports())
|
||||
|
||||
|
||||
@pytest.mark.parametrize("num_segments", [-1, 2, (0, 1), 0, 1, (0, 3)])
|
||||
def test_params_errors(simple_canvas, num_segments):
|
||||
"""Test parameter error exceptions.
|
||||
|
||||
"""
|
||||
line = Line()
|
||||
simple_canvas.canvas.add(line)
|
||||
segment = Segment(line, simple_canvas.view)
|
||||
with pytest.raises(ValueError):
|
||||
if isinstance(num_segments, tuple):
|
||||
segment.split_segment(0)
|
||||
segment.merge_segment(num_segments[0], num_segments[1])
|
||||
elif num_segments == 0:
|
||||
assert 2 == len(line.handles())
|
||||
segment.merge_segment(0)
|
||||
else:
|
||||
segment.split_segment(0)
|
||||
segment.merge_segment(num_segments)
|
||||
|
||||
|
||||
def test_handle_finder(seg):
|
||||
finder = HandleFinder(seg.line, seg.view)
|
||||
assert type(finder) is SegmentHandleFinder, type(finder)
|
||||
|
@ -1,15 +1,15 @@
|
||||
"""
|
||||
Unit tests for Gaphas' solver.
|
||||
"""
|
||||
from __future__ import print_function
|
||||
from __future__ import division
|
||||
"""Test constraint solver.
|
||||
|
||||
"""
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
import unittest
|
||||
from timeit import Timer
|
||||
|
||||
from gaphas.solver import Solver, Variable
|
||||
from gaphas.constraint import EquationConstraint, EqualsConstraint, LessThanConstraint
|
||||
import pytest
|
||||
|
||||
from gaphas.constraint import EquationConstraint, EqualsConstraint, LessThanConstraint
|
||||
from gaphas.solver import Solver, Variable
|
||||
|
||||
SETUP = """
|
||||
from gaphas.solver import Solver, Variable
|
||||
@ -25,143 +25,118 @@ REPEAT = 30
|
||||
NUMBER = 1000
|
||||
|
||||
|
||||
class WeakestVariableTestCase(unittest.TestCase):
|
||||
class SolverFixture(object):
|
||||
def __init__(self):
|
||||
self.solver = Solver()
|
||||
self.a = Variable(1, 30)
|
||||
self.b = Variable(2, 10)
|
||||
self.c = Variable(3, 10)
|
||||
self.c_eq = EquationConstraint(
|
||||
lambda a, b, c: a + b + c, a=self.a, b=self.b, c=self.c
|
||||
)
|
||||
self.solver.add_constraint(self.c_eq)
|
||||
|
||||
|
||||
@pytest.fixture(name="solv")
|
||||
def solver_fixture():
|
||||
return SolverFixture()
|
||||
|
||||
|
||||
def test_weakest_list(solv):
|
||||
"""Test weakest list.
|
||||
|
||||
"""
|
||||
Test weakest variable calculation.
|
||||
assert solv.b in solv.c_eq._weakest
|
||||
assert solv.c in solv.c_eq._weakest
|
||||
|
||||
|
||||
def test_weakest_list_order(solv):
|
||||
"""Test weakest list order.
|
||||
|
||||
"""
|
||||
weakest = [el for el in solv.c_eq._weakest]
|
||||
solv.a.value = 4
|
||||
|
||||
def test_weakest_list(self):
|
||||
"""Test weakest list"""
|
||||
solver = Solver()
|
||||
a = Variable(1, 30)
|
||||
b = Variable(2, 10)
|
||||
c = Variable(3, 10)
|
||||
assert solv.c_eq._weakest == weakest # Does not change if non-weak variable changed
|
||||
|
||||
c_eq = EquationConstraint(lambda a, b, c: a + b + c, a=a, b=b, c=c)
|
||||
solver.add_constraint(c_eq)
|
||||
solv.b.value = 5
|
||||
assert solv.c_eq.weakest() == solv.c
|
||||
|
||||
# because of kwargs above we cannot test by the order of arguments
|
||||
self.assertTrue(b in c_eq._weakest)
|
||||
self.assertTrue(c in c_eq._weakest)
|
||||
solv.b.value = 6
|
||||
assert solv.c_eq.weakest() == solv.c
|
||||
|
||||
def test_weakest_list_order(self):
|
||||
"""Test weakest list order"""
|
||||
solver = Solver()
|
||||
a = Variable(1, 30)
|
||||
b = Variable(2, 10)
|
||||
c = Variable(3, 10)
|
||||
# b changed above, now change a - all weakest variables changed return the
|
||||
# oldest changed variable
|
||||
solv.c.value = 6
|
||||
assert solv.c_eq.weakest() == solv.b
|
||||
|
||||
c_eq = EquationConstraint(lambda a, b, c: a + b + c, a=a, b=b, c=c)
|
||||
solver.add_constraint(c_eq)
|
||||
|
||||
weakest = [el for el in c_eq._weakest]
|
||||
a.value = 4
|
||||
|
||||
self.assertEqual(
|
||||
c_eq._weakest, weakest
|
||||
) # does not change if non-weak variable changed
|
||||
|
||||
b.value = 5
|
||||
self.assertEqual(c_eq.weakest(), c)
|
||||
|
||||
b.value = 6
|
||||
self.assertEqual(c_eq.weakest(), c)
|
||||
|
||||
# b changed above, now change a - all weakest variables changed
|
||||
# return the oldest changed variable then
|
||||
c.value = 6
|
||||
self.assertEqual(c_eq.weakest(), b)
|
||||
|
||||
b.value = 6
|
||||
self.assertEqual(c_eq.weakest(), c)
|
||||
|
||||
def test_strength_change(self):
|
||||
"""Test strength change"""
|
||||
solver = Solver()
|
||||
a = Variable(1, 30)
|
||||
b = Variable(2, 10)
|
||||
c = Variable(3, 10)
|
||||
|
||||
c_eq = EquationConstraint(lambda a, b, c: a + b + c, a=a, b=b, c=c)
|
||||
solver.add_constraint(c_eq)
|
||||
|
||||
b.strength = 9
|
||||
self.assertEqual(c_eq._weakest, [b])
|
||||
solv.b.value = 6
|
||||
assert solv.c_eq.weakest() == solv.c
|
||||
|
||||
|
||||
class SizeTestCase(unittest.TestCase):
|
||||
def test_strength_change(solv):
|
||||
"""Test strength change.
|
||||
|
||||
"""
|
||||
Test size related constraints, i.e. minimal size.
|
||||
solv.b.strength = 9
|
||||
assert solv.c_eq._weakest == [solv.b]
|
||||
|
||||
|
||||
def test_min_size(solv):
|
||||
"""Test minimal size constraint.
|
||||
|
||||
"""
|
||||
v1 = Variable(0)
|
||||
v2 = Variable(10)
|
||||
v3 = Variable(10)
|
||||
c1 = EqualsConstraint(a=v2, b=v3)
|
||||
c2 = LessThanConstraint(smaller=v1, bigger=v3, delta=10)
|
||||
solv.solver.add_constraint(c1)
|
||||
solv.solver.add_constraint(c2)
|
||||
|
||||
def test_min_size(self):
|
||||
"""Test minimal size constraint"""
|
||||
solver = Solver()
|
||||
v1 = Variable(0)
|
||||
v2 = Variable(10)
|
||||
v3 = Variable(10)
|
||||
c1 = EqualsConstraint(a=v2, b=v3)
|
||||
c2 = LessThanConstraint(smaller=v1, bigger=v3, delta=10)
|
||||
solver.add_constraint(c1)
|
||||
solver.add_constraint(c2)
|
||||
# Check everything is ok on start
|
||||
solv.solver.solve()
|
||||
assert 0 == v1
|
||||
assert 10 == v2
|
||||
assert 10 == v3
|
||||
|
||||
# check everyting is ok on start
|
||||
solver.solve()
|
||||
self.assertEqual(0, v1)
|
||||
self.assertEqual(10, v2)
|
||||
self.assertEqual(10, v3)
|
||||
# Change v1 to 2, after solve it should be 0 again due to LT constraint
|
||||
v1.value = 2
|
||||
solv.solver.solve()
|
||||
|
||||
# change v1 to 2, after solve it should be 0 again due to LT
|
||||
# constraint
|
||||
v1.value = 2
|
||||
solver.solve()
|
||||
assert 0 == v1
|
||||
assert 10 == v2
|
||||
assert 10 == v3
|
||||
|
||||
self.assertEqual(0, v1)
|
||||
self.assertEqual(10, v2)
|
||||
self.assertEqual(10, v3)
|
||||
# Change v3 to 20, after solve v2 will follow thanks to EQ constraint
|
||||
v3.value = 20
|
||||
solv.solver.solve()
|
||||
|
||||
# change v3 to 20, after solve v2 will follow thanks to EQ
|
||||
# constraint
|
||||
v3.value = 20
|
||||
solver.solve()
|
||||
assert 0 == v1
|
||||
assert 20 == v2
|
||||
assert 20 == v3
|
||||
|
||||
self.assertEqual(0, v1)
|
||||
self.assertEqual(20, v2)
|
||||
self.assertEqual(20, v3)
|
||||
# Change v3 to 0, after solve it should be 10 due to LT.delta = 10, v2
|
||||
# should also be 10 due to EQ constraint
|
||||
v3.value = 0
|
||||
solv.solver.solve()
|
||||
|
||||
# change v3 to 0, after solve it shoul be 10 due to LT.delta = 10,
|
||||
# v2 should also be 10 due to EQ constraint
|
||||
v3.value = 0
|
||||
solver.solve()
|
||||
|
||||
self.assertEqual(0, v1)
|
||||
self.assertEqual(10, v2)
|
||||
self.assertEqual(10, v3)
|
||||
assert 0 == v1
|
||||
assert 10 == v2
|
||||
assert 10 == v3
|
||||
|
||||
|
||||
class SolverSpeedTestCase(unittest.TestCase):
|
||||
def test_speed_run_weakest():
|
||||
"""Speed test for weakest variable.
|
||||
|
||||
"""
|
||||
Solver speed tests.
|
||||
"""
|
||||
|
||||
def _test_speed_run_weakest(self):
|
||||
"""
|
||||
Speed test for weakest variable.
|
||||
"""
|
||||
|
||||
results = Timer(
|
||||
setup=SETUP,
|
||||
stmt="""
|
||||
results = Timer(
|
||||
setup=SETUP,
|
||||
stmt="""
|
||||
v1.value = 5.0
|
||||
c_eq.weakest()""",
|
||||
).repeat(repeat=REPEAT, number=NUMBER)
|
||||
).repeat(repeat=REPEAT, number=NUMBER)
|
||||
|
||||
# Print the average of the best 10 runs:
|
||||
results.sort()
|
||||
print("[Avg: %gms]" % ((sum(results[:10]) / 10)) * 1000)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
# vim:sw=4:et:ai
|
||||
# Print the average of the best 10 runs:
|
||||
results.sort()
|
||||
print("[Avg: %gms]" % (sum(results[:10]) / 10) * 1000)
|
||||
|
@ -1,5 +1,4 @@
|
||||
import sys
|
||||
import unittest
|
||||
from builtins import object
|
||||
|
||||
from gaphas.state import reversible_pair, observed, _reverse
|
||||
@ -22,19 +21,19 @@ class SList(object):
|
||||
self.list.remove(self.list.index(node))
|
||||
|
||||
|
||||
class StateTestCase(unittest.TestCase):
|
||||
def test_adding_pair(self):
|
||||
"""Test adding reversible pair
|
||||
"""
|
||||
reversible_pair(
|
||||
SList.add,
|
||||
SList.remove,
|
||||
bind1={"before": lambda self, node: self.list[self.list.index(node) + 1]},
|
||||
)
|
||||
def test_adding_pair():
|
||||
"""Test adding reversible pair.
|
||||
|
||||
if sys.version_info.major >= 3: # Modern Python
|
||||
self.assertTrue(SList.add in _reverse)
|
||||
self.assertTrue(SList.remove in _reverse)
|
||||
else: # Legacy Python
|
||||
self.assertTrue(SList.add.__func__ in _reverse)
|
||||
self.assertTrue(SList.remove.__func__ in _reverse)
|
||||
"""
|
||||
reversible_pair(
|
||||
SList.add,
|
||||
SList.remove,
|
||||
bind1={"before": lambda self, node: self.list[self.list.index(node) + 1]},
|
||||
)
|
||||
|
||||
if sys.version_info.major >= 3: # Modern Python
|
||||
assert SList.add in _reverse
|
||||
assert SList.remove in _reverse
|
||||
else: # Legacy Python
|
||||
assert SList.add.__func__ in _reverse
|
||||
assert SList.remove.__func__ in _reverse
|
||||
|
@ -1,251 +1,167 @@
|
||||
"""Test all the tools.
|
||||
|
||||
"""
|
||||
Test all the tools provided by gaphas.
|
||||
"""
|
||||
|
||||
import unittest
|
||||
|
||||
from gi.repository import Gtk
|
||||
|
||||
from gaphas.canvas import Canvas
|
||||
from gaphas.canvas import Context
|
||||
from gaphas.constraint import LineConstraint
|
||||
from gaphas.examples import Box
|
||||
from gaphas.item import Line
|
||||
from gaphas.tool import ConnectHandleTool
|
||||
from gaphas.view import GtkView
|
||||
|
||||
Event = Context
|
||||
|
||||
|
||||
def simple_canvas(self):
|
||||
# Test handle connection tool glue method
|
||||
|
||||
|
||||
def test_item_and_port_glue(simple_canvas):
|
||||
"""Test glue operation to an item and its ports.
|
||||
|
||||
"""
|
||||
This decorator adds view, canvas and handle connection tool to a test
|
||||
case. Two boxes and a line are added to the canvas as well.
|
||||
ports = simple_canvas.box1.ports()
|
||||
|
||||
# Glue to port nw-ne
|
||||
sink = simple_canvas.tool.glue(simple_canvas.line, simple_canvas.head, (120, 50))
|
||||
assert sink.item == simple_canvas.box1
|
||||
assert ports[0] == sink.port
|
||||
|
||||
# Glue to port ne-se
|
||||
sink = simple_canvas.tool.glue(simple_canvas.line, simple_canvas.head, (140, 70))
|
||||
assert sink.item == simple_canvas.box1
|
||||
assert ports[1] == sink.port
|
||||
|
||||
# Glue to port se-sw
|
||||
sink = simple_canvas.tool.glue(simple_canvas.line, simple_canvas.head, (120, 90))
|
||||
assert sink.item == simple_canvas.box1
|
||||
assert ports[2] == sink.port
|
||||
|
||||
# Glue to port sw-nw
|
||||
sink = simple_canvas.tool.glue(simple_canvas.line, simple_canvas.head, (100, 70))
|
||||
assert sink.item == simple_canvas.box1
|
||||
assert ports[3] == sink.port
|
||||
|
||||
|
||||
def test_failed_glue(simple_canvas):
|
||||
"""Test glue from too far distance.
|
||||
|
||||
"""
|
||||
self.canvas = Canvas()
|
||||
|
||||
self.box1 = Box()
|
||||
self.canvas.add(self.box1)
|
||||
self.box1.matrix.translate(100, 50)
|
||||
self.box1.width = 40
|
||||
self.box1.height = 40
|
||||
self.box1.request_update()
|
||||
|
||||
self.box2 = Box()
|
||||
self.canvas.add(self.box2)
|
||||
self.box2.matrix.translate(100, 150)
|
||||
self.box2.width = 50
|
||||
self.box2.height = 50
|
||||
self.box2.request_update()
|
||||
|
||||
self.line = Line()
|
||||
self.head = self.line.handles()[0]
|
||||
self.tail = self.line.handles()[-1]
|
||||
self.tail.pos = 100, 100
|
||||
self.canvas.add(self.line)
|
||||
|
||||
self.canvas.update_now()
|
||||
self.view = GtkView()
|
||||
self.view.canvas = self.canvas
|
||||
|
||||
win = Gtk.Window()
|
||||
win.add(self.view)
|
||||
self.view.show()
|
||||
self.view.update()
|
||||
win.show()
|
||||
|
||||
self.tool = ConnectHandleTool(self.view)
|
||||
sink = simple_canvas.tool.glue(simple_canvas.line, simple_canvas.head, (90, 50))
|
||||
assert sink is None
|
||||
|
||||
|
||||
class ConnectHandleToolGlueTestCase(unittest.TestCase):
|
||||
"""
|
||||
Test handle connection tool glue method.
|
||||
def test_glue_no_port_no_can_glue(simple_canvas):
|
||||
"""Test no glue with no port.
|
||||
|
||||
Test if glue method does not call ConnectHandleTool.can_glue method when
|
||||
port is not found.
|
||||
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
simple_canvas(self)
|
||||
class Tool(ConnectHandleTool):
|
||||
def __init__(self, *args):
|
||||
super(Tool, self).__init__(*args)
|
||||
self._calls = 0
|
||||
|
||||
def test_item_and_port_glue(self):
|
||||
"""Test glue operation to an item and its ports"""
|
||||
def can_glue(self, *args):
|
||||
self._calls += 1
|
||||
|
||||
ports = self.box1.ports()
|
||||
|
||||
# glue to port nw-ne
|
||||
sink = self.tool.glue(self.line, self.head, (120, 50))
|
||||
self.assertEqual(sink.item, self.box1)
|
||||
self.assertEqual(ports[0], sink.port)
|
||||
|
||||
# glue to port ne-se
|
||||
sink = self.tool.glue(self.line, self.head, (140, 70))
|
||||
self.assertEqual(sink.item, self.box1)
|
||||
self.assertEqual(ports[1], sink.port)
|
||||
|
||||
# glue to port se-sw
|
||||
sink = self.tool.glue(self.line, self.head, (120, 90))
|
||||
self.assertEqual(sink.item, self.box1)
|
||||
self.assertEqual(ports[2], sink.port)
|
||||
|
||||
# glue to port sw-nw
|
||||
sink = self.tool.glue(self.line, self.head, (100, 70))
|
||||
self.assertEqual(sink.item, self.box1)
|
||||
self.assertEqual(ports[3], sink.port)
|
||||
|
||||
def test_failed_glue(self):
|
||||
"""Test glue from too far distance"""
|
||||
sink = self.tool.glue(self.line, self.head, (90, 50))
|
||||
self.assertTrue(sink is None)
|
||||
|
||||
# def test_glue_call_can_glue_once(self):
|
||||
# """Test if glue method calls can glue once only
|
||||
#
|
||||
# Box has 4 ports. Every port is examined once per
|
||||
# ConnectHandleTool.glue method call. The purpose of this test is to
|
||||
# assure that ConnectHandleTool.can_glue is called once (for the
|
||||
# found port), it cannot be called four times (once for every port).
|
||||
# """
|
||||
#
|
||||
# # count ConnectHandleTool.can_glue calls
|
||||
# class Tool(ConnectHandleTool):
|
||||
# def __init__(self, *args):
|
||||
# super(Tool, self).__init__(*args)
|
||||
# self._calls = 0
|
||||
#
|
||||
# def can_glue(self, *args):
|
||||
# self._calls += 1
|
||||
# return True
|
||||
#
|
||||
# tool = Tool(self.view)
|
||||
# item, port = tool.glue(self.line, self.head, (120, 50))
|
||||
# assert item and port
|
||||
# self.assertEqual(1, tool._calls)
|
||||
|
||||
# def test_glue_cannot_glue(self):
|
||||
# """Test if glue method respects ConnectHandleTool.can_glue method"""
|
||||
#
|
||||
# class Tool(ConnectHandleTool):
|
||||
# def can_glue(self, *args):
|
||||
# return False
|
||||
#
|
||||
# tool = Tool(self.view)
|
||||
# item, port = tool.glue(self.line, self.head, (120, 50))
|
||||
# self.assertTrue(item is None, item)
|
||||
# self.assertTrue(port is None, port)
|
||||
|
||||
def test_glue_no_port_no_can_glue(self):
|
||||
"""Test if glue method does not call ConnectHandleTool.can_glue method when port is not found"""
|
||||
|
||||
class Tool(ConnectHandleTool):
|
||||
def __init__(self, *args):
|
||||
super(Tool, self).__init__(*args)
|
||||
self._calls = 0
|
||||
|
||||
def can_glue(self, *args):
|
||||
self._calls += 1
|
||||
|
||||
tool = Tool(self.view)
|
||||
# at 300, 50 there should be no item
|
||||
sink = tool.glue(self.line, self.head, (300, 50))
|
||||
assert sink is None
|
||||
self.assertEqual(0, tool._calls)
|
||||
tool = Tool(simple_canvas.view)
|
||||
# At 300, 50 there should be no item
|
||||
sink = tool.glue(simple_canvas.line, simple_canvas.head, (300, 50))
|
||||
assert sink is None
|
||||
assert 0 == tool._calls
|
||||
|
||||
|
||||
class ConnectHandleToolConnectTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
simple_canvas(self)
|
||||
def test_connect(simple_canvas):
|
||||
"""Test connection to an item.
|
||||
|
||||
def _get_line(self):
|
||||
line = Line()
|
||||
head = line.handles()[0]
|
||||
self.canvas.add(line)
|
||||
return line, head
|
||||
"""
|
||||
line, head = simple_canvas.line, simple_canvas.head
|
||||
simple_canvas.tool.connect(line, head, (120, 50))
|
||||
cinfo = simple_canvas.canvas.get_connection(head)
|
||||
assert cinfo is not None
|
||||
assert simple_canvas.box1 == cinfo.connected
|
||||
assert cinfo.port is simple_canvas.box1.ports()[0], "port %s" % cinfo.port
|
||||
assert isinstance(cinfo.constraint, LineConstraint)
|
||||
# No default callback defined:
|
||||
assert cinfo.callback is None
|
||||
|
||||
def test_connect(self):
|
||||
"""Test connection to an item"""
|
||||
line, head = self._get_line()
|
||||
self.tool.connect(line, head, (120, 50))
|
||||
cinfo = self.canvas.get_connection(head)
|
||||
self.assertTrue(cinfo is not None)
|
||||
self.assertEqual(self.box1, cinfo.connected)
|
||||
self.assertTrue(cinfo.port is self.box1.ports()[0], "port %s" % cinfo.port)
|
||||
self.assertTrue(isinstance(cinfo.constraint, LineConstraint))
|
||||
# No default callback defined:
|
||||
self.assertTrue(cinfo.callback is None)
|
||||
|
||||
line, head = self._get_line()
|
||||
self.tool.connect(line, head, (90, 50))
|
||||
cinfo2 = self.canvas.get_connection(head)
|
||||
self.assertTrue(cinfo is not cinfo2, cinfo2)
|
||||
self.assertTrue(cinfo2 is None, cinfo2)
|
||||
|
||||
def test_reconnect_another(self):
|
||||
"""Test reconnection to another item"""
|
||||
line, head = self._get_line()
|
||||
self.tool.connect(line, head, (120, 50))
|
||||
cinfo = self.canvas.get_connection(head)
|
||||
assert cinfo is not None
|
||||
item = cinfo.connected
|
||||
port = cinfo.port
|
||||
constraint = cinfo.constraint
|
||||
|
||||
assert item == self.box1
|
||||
assert port == self.box1.ports()[0]
|
||||
assert item != self.box2
|
||||
|
||||
# connect to box2, handle's connected item and connection data
|
||||
# should differ
|
||||
self.tool.connect(line, head, (120, 150))
|
||||
cinfo = self.canvas.get_connection(head)
|
||||
assert cinfo is not None
|
||||
self.assertEqual(self.box2, cinfo.connected)
|
||||
self.assertEqual(self.box2.ports()[0], cinfo.port)
|
||||
|
||||
# old connection does not exist
|
||||
self.assertNotEqual(item, cinfo.connected)
|
||||
self.assertNotEqual(constraint, cinfo.constraint)
|
||||
|
||||
def test_reconnect_same(self):
|
||||
"""Test reconnection to same item"""
|
||||
line, head = self._get_line()
|
||||
self.tool.connect(line, head, (120, 50))
|
||||
cinfo = self.canvas.get_connection(head)
|
||||
assert cinfo is not None
|
||||
item = cinfo.connected
|
||||
port = cinfo.port
|
||||
constraint = cinfo.constraint
|
||||
|
||||
assert item == self.box1
|
||||
assert item != self.box2
|
||||
|
||||
# connect to box1 again, handle's connected item and port should be
|
||||
# the same but connection constraint will differ
|
||||
connected = self.tool.connect(line, head, (120, 50))
|
||||
cinfo = self.canvas.get_connection(head)
|
||||
assert cinfo is not None
|
||||
self.assertEqual(self.box1, cinfo.connected)
|
||||
self.assertEqual(self.box1.ports()[0], cinfo.port)
|
||||
self.assertNotEqual(constraint, cinfo.constraint)
|
||||
|
||||
def xtest_find_port(self):
|
||||
"""Test finding a port
|
||||
"""
|
||||
line, head = self._get_line()
|
||||
p1, p2, p3, p4 = self.box1.ports()
|
||||
|
||||
head.pos = 110, 50
|
||||
port = self.tool.find_port(line, head, self.box1)
|
||||
self.assertEqual(p1, port)
|
||||
|
||||
head.pos = 140, 60
|
||||
port = self.tool.find_port(line, head, self.box1)
|
||||
self.assertEqual(p2, port)
|
||||
|
||||
head.pos = 110, 95
|
||||
port = self.tool.find_port(line, head, self.box1)
|
||||
self.assertEqual(p3, port)
|
||||
|
||||
head.pos = 100, 55
|
||||
port = self.tool.find_port(line, head, self.box1)
|
||||
self.assertEqual(p4, port)
|
||||
line, head = simple_canvas.line, simple_canvas.head
|
||||
simple_canvas.tool.connect(line, head, (90, 50))
|
||||
cinfo2 = simple_canvas.canvas.get_connection(head)
|
||||
assert cinfo is not cinfo2, cinfo2
|
||||
assert cinfo2 is None, cinfo2
|
||||
|
||||
|
||||
# vim: sw=4:et:ai
|
||||
def test_reconnect_another(simple_canvas):
|
||||
"""Test reconnection to another item.
|
||||
|
||||
"""
|
||||
line, head = simple_canvas.line, simple_canvas.head
|
||||
simple_canvas.tool.connect(line, head, (120, 50))
|
||||
cinfo = simple_canvas.canvas.get_connection(head)
|
||||
assert cinfo is not None
|
||||
item = cinfo.connected
|
||||
port = cinfo.port
|
||||
constraint = cinfo.constraint
|
||||
|
||||
assert item == simple_canvas.box1
|
||||
assert port == simple_canvas.box1.ports()[0]
|
||||
assert item != simple_canvas.box2
|
||||
|
||||
# Connect to box2, handle's connected item and connection data should
|
||||
# differ
|
||||
simple_canvas.tool.connect(line, head, (120, 150))
|
||||
cinfo = simple_canvas.canvas.get_connection(head)
|
||||
assert cinfo is not None
|
||||
assert simple_canvas.box2 == cinfo.connected
|
||||
assert simple_canvas.box2.ports()[0] == cinfo.port
|
||||
|
||||
# Old connection does not exist
|
||||
assert item != cinfo.connected
|
||||
assert constraint != cinfo.constraint
|
||||
|
||||
|
||||
def test_reconnect_same(simple_canvas):
|
||||
"""Test reconnection to same item.
|
||||
|
||||
"""
|
||||
line, head = simple_canvas.line, simple_canvas.head
|
||||
simple_canvas.tool.connect(line, head, (120, 50))
|
||||
cinfo = simple_canvas.canvas.get_connection(head)
|
||||
assert cinfo is not None
|
||||
item = cinfo.connected
|
||||
constraint = cinfo.constraint
|
||||
|
||||
assert item == simple_canvas.box1
|
||||
assert item != simple_canvas.box2
|
||||
|
||||
# Connect to box1 again, handle's connected item and port should be the
|
||||
# same but connection constraint will differ
|
||||
simple_canvas.tool.connect(line, head, (120, 50))
|
||||
cinfo = simple_canvas.canvas.get_connection(head)
|
||||
assert cinfo is not None
|
||||
assert simple_canvas.box1 == cinfo.connected
|
||||
assert simple_canvas.box1.ports()[0] == cinfo.port
|
||||
assert constraint != cinfo.constraint
|
||||
|
||||
|
||||
def xtest_find_port(simple_canvas):
|
||||
"""Test finding a port.
|
||||
|
||||
"""
|
||||
line, head = simple_canvas.line, simple_canvas.head
|
||||
p1, p2, p3, p4 = simple_canvas.box1.ports()
|
||||
|
||||
head.pos = 110, 50
|
||||
port = simple_canvas.tool.find_port(line, head, simple_canvas.box1)
|
||||
assert p1 == port
|
||||
|
||||
head.pos = 140, 60
|
||||
port = simple_canvas.tool.find_port(line, head, simple_canvas.box1)
|
||||
assert p2 == port
|
||||
|
||||
head.pos = 110, 95
|
||||
port = simple_canvas.tool.find_port(line, head, simple_canvas.box1)
|
||||
assert p3 == port
|
||||
|
||||
head.pos = 100, 55
|
||||
port = simple_canvas.tool.find_port(line, head, simple_canvas.box1)
|
||||
assert p4 == port
|
||||
|
@ -1,164 +1,145 @@
|
||||
import unittest
|
||||
import pytest
|
||||
from gaphas.tree import Tree
|
||||
|
||||
|
||||
class TreeTestCase(unittest.TestCase):
|
||||
def test_add(self):
|
||||
"""
|
||||
Test creating node trees.
|
||||
"""
|
||||
tree = Tree()
|
||||
n1 = "n1"
|
||||
n2 = "n2"
|
||||
n3 = "n3"
|
||||
|
||||
tree.add(n1)
|
||||
assert len(tree._nodes) == 1
|
||||
assert len(tree._children) == 2
|
||||
assert len(tree._children[None]) == 1
|
||||
assert len(tree._children[n1]) == 0
|
||||
|
||||
tree.add(n2)
|
||||
tree.add(n3, parent=n1)
|
||||
assert len(tree._nodes) == 3
|
||||
assert len(tree._children) == 4
|
||||
assert len(tree._children[None]) == 2
|
||||
assert len(tree._children[n1]) == 1
|
||||
assert len(tree._children[n2]) == 0
|
||||
assert len(tree._children[n2]) == 0
|
||||
assert tree._nodes == [n1, n3, n2]
|
||||
|
||||
n4 = "n4"
|
||||
tree.add(n4, parent=n3)
|
||||
assert tree._nodes == [n1, n3, n4, n2]
|
||||
|
||||
n5 = "n5"
|
||||
tree.add(n5, parent=n3)
|
||||
assert tree._nodes == [n1, n3, n4, n5, n2]
|
||||
|
||||
n6 = "n6"
|
||||
tree.add(n6, parent=n2)
|
||||
assert tree._nodes == [n1, n3, n4, n5, n2, n6]
|
||||
|
||||
n7 = "n7"
|
||||
tree.add(n7, parent=n1)
|
||||
assert len(tree._children) == 8
|
||||
assert tree._nodes == [n1, n3, n4, n5, n7, n2, n6]
|
||||
assert tree.get_parent(n7) is n1
|
||||
assert tree.get_parent(n6) is n2
|
||||
assert tree.get_parent(n5) is n3
|
||||
assert tree.get_parent(n4) is n3
|
||||
assert tree.get_parent(n3) is n1
|
||||
assert tree.get_parent(n2) is None
|
||||
assert tree.get_parent(n1) is None
|
||||
|
||||
def test_add_on_index(self):
|
||||
tree = Tree()
|
||||
n1 = "n1"
|
||||
n2 = "n2"
|
||||
n3 = "n3"
|
||||
n4 = "n4"
|
||||
n5 = "n5"
|
||||
|
||||
tree.add(n1)
|
||||
tree.add(n2)
|
||||
tree.add(n3, index=1)
|
||||
assert tree.get_children(None) == [n1, n3, n2], tree.get_children(None)
|
||||
assert tree.nodes == [n1, n3, n2], tree.nodes
|
||||
|
||||
tree.add(n4, parent=n3)
|
||||
tree.add(n5, parent=n3, index=0)
|
||||
assert tree.get_children(None) == [n1, n3, n2], tree.get_children(None)
|
||||
assert tree.nodes == [n1, n3, n5, n4, n2], tree.nodes
|
||||
assert tree.get_children(n3) == [n5, n4], tree.get_children(n3)
|
||||
|
||||
def test_remove(self):
|
||||
"""
|
||||
Test removal of nodes.
|
||||
"""
|
||||
tree = Tree()
|
||||
n1 = "n1"
|
||||
n2 = "n2"
|
||||
n3 = "n3"
|
||||
n4 = "n4"
|
||||
n5 = "n5"
|
||||
|
||||
tree.add(n1)
|
||||
tree.add(n2)
|
||||
tree.add(n3, parent=n1)
|
||||
tree.add(n4, parent=n3)
|
||||
tree.add(n5, parent=n4)
|
||||
|
||||
assert tree._nodes == [n1, n3, n4, n5, n2]
|
||||
|
||||
all_ch = list(tree.get_all_children(n1))
|
||||
assert all_ch == [n3, n4, n5], all_ch
|
||||
|
||||
tree.remove(n4)
|
||||
assert tree._nodes == [n1, n3, n2]
|
||||
|
||||
tree.remove(n1)
|
||||
assert len(tree._children) == 2
|
||||
assert tree._children[None] == [n2]
|
||||
assert tree._children[n2] == []
|
||||
assert tree._nodes == [n2]
|
||||
|
||||
def test_siblings(self):
|
||||
tree = Tree()
|
||||
n1 = "n1"
|
||||
n2 = "n2"
|
||||
n3 = "n3"
|
||||
|
||||
tree.add(n1)
|
||||
tree.add(n2)
|
||||
tree.add(n3)
|
||||
|
||||
assert tree.get_next_sibling(n1) is n2
|
||||
assert tree.get_next_sibling(n2) is n3
|
||||
try:
|
||||
tree.get_next_sibling(n3)
|
||||
except IndexError:
|
||||
pass # okay
|
||||
else:
|
||||
raise AssertionError(
|
||||
"Index should be out of range, not %s" % tree.get_next_sibling(n3)
|
||||
)
|
||||
|
||||
assert tree.get_previous_sibling(n3) is n2
|
||||
assert tree.get_previous_sibling(n2) is n1
|
||||
try:
|
||||
tree.get_previous_sibling(n1)
|
||||
except IndexError:
|
||||
pass # okay
|
||||
else:
|
||||
raise AssertionError(
|
||||
"Index should be out of range, not %s" % tree.get_previous_sibling(n1)
|
||||
)
|
||||
|
||||
def test_reparent(self):
|
||||
tree = Tree()
|
||||
n1 = "n1"
|
||||
n2 = "n2"
|
||||
n3 = "n3"
|
||||
n4 = "n4"
|
||||
n5 = "n5"
|
||||
|
||||
tree.add(n1)
|
||||
tree.add(n2)
|
||||
tree.add(n3)
|
||||
tree.add(n4, parent=n2)
|
||||
tree.add(n5, parent=n4)
|
||||
|
||||
assert tree.nodes == [n1, n2, n4, n5, n3], tree.nodes
|
||||
|
||||
tree.reparent(n4, parent=n1, index=0)
|
||||
assert tree.nodes == [n1, n4, n5, n2, n3], tree.nodes
|
||||
assert tree.get_children(n2) == [], tree.get_children(n2)
|
||||
assert tree.get_children(n1) == [n4], tree.get_children(n1)
|
||||
assert tree.get_children(n4) == [n5], tree.get_children(n4)
|
||||
|
||||
tree.reparent(n4, parent=None, index=0)
|
||||
assert tree.nodes == [n4, n5, n1, n2, n3], tree.nodes
|
||||
@pytest.fixture()
|
||||
def tree_fixture():
|
||||
node = ["n0", "n1", "n2", "n3", "n4", "n5", "n6", "n7"]
|
||||
return Tree(), node
|
||||
|
||||
|
||||
# vi:sw=4:et:ai
|
||||
def test_add(tree_fixture):
|
||||
""" Test creating node trees.
|
||||
|
||||
"""
|
||||
tree = tree_fixture[0]
|
||||
n = tree_fixture[1]
|
||||
tree.add(n[1])
|
||||
assert len(tree._nodes) == 1
|
||||
assert len(tree._children) == 2
|
||||
assert len(tree._children[None]) == 1
|
||||
assert len(tree._children[n[1]]) == 0
|
||||
|
||||
tree.add(n[2])
|
||||
tree.add(n[3], parent=n[1])
|
||||
assert len(tree._nodes) == 3
|
||||
assert len(tree._children) == 4
|
||||
assert len(tree._children[None]) == 2
|
||||
assert len(tree._children[n[1]]) == 1
|
||||
assert len(tree._children[n[2]]) == 0
|
||||
assert len(tree._children[n[2]]) == 0
|
||||
assert tree._nodes == [n[1], n[3], n[2]]
|
||||
|
||||
tree.add(n[4], parent=n[3])
|
||||
assert tree._nodes == [n[1], n[3], n[4], n[2]]
|
||||
|
||||
tree.add(n[5], parent=n[3])
|
||||
assert tree._nodes == [n[1], n[3], n[4], n[5], n[2]]
|
||||
|
||||
tree.add(n[6], parent=n[2])
|
||||
assert tree._nodes == [n[1], n[3], n[4], n[5], n[2], n[6]]
|
||||
|
||||
tree.add(n[7], parent=n[1])
|
||||
assert len(tree._children) == 8
|
||||
assert tree._nodes == [n[1], n[3], n[4], n[5], n[7], n[2], n[6]]
|
||||
assert tree.get_parent(n[7]) is n[1]
|
||||
assert tree.get_parent(n[6]) is n[2]
|
||||
assert tree.get_parent(n[5]) is n[3]
|
||||
assert tree.get_parent(n[4]) is n[3]
|
||||
assert tree.get_parent(n[3]) is n[1]
|
||||
assert tree.get_parent(n[2]) is None
|
||||
assert tree.get_parent(n[1]) is None
|
||||
|
||||
|
||||
def test_add_on_index(tree_fixture):
|
||||
tree = tree_fixture[0]
|
||||
n = tree_fixture[1]
|
||||
tree.add(n[1])
|
||||
tree.add(n[2])
|
||||
tree.add(n[3], index=1)
|
||||
assert tree.get_children(None) == [n[1], n[3], n[2]], tree.get_children(None)
|
||||
assert tree.nodes == [n[1], n[3], n[2]], tree.nodes
|
||||
|
||||
tree.add(n[4], parent=n[3])
|
||||
tree.add(n[5], parent=n[3], index=0)
|
||||
assert tree.get_children(None) == [n[1], n[3], n[2]], tree.get_children(None)
|
||||
assert tree.nodes == [n[1], n[3], n[5], n[4], n[2]], tree.nodes
|
||||
assert tree.get_children(n[3]) == [n[5], n[4]], tree.get_children(n[3])
|
||||
|
||||
|
||||
def test_remove(tree_fixture):
|
||||
"""Test removal of nodes.
|
||||
|
||||
"""
|
||||
tree = tree_fixture[0]
|
||||
n = tree_fixture[1]
|
||||
tree.add(n[1])
|
||||
tree.add(n[2])
|
||||
tree.add(n[3], parent=n[1])
|
||||
tree.add(n[4], parent=n[3])
|
||||
tree.add(n[5], parent=n[4])
|
||||
|
||||
assert tree._nodes == [n[1], n[3], n[4], n[5], n[2]]
|
||||
|
||||
all_ch = list(tree.get_all_children(n[1]))
|
||||
assert all_ch == [n[3], n[4], n[5]], all_ch
|
||||
|
||||
tree.remove(n[4])
|
||||
assert tree._nodes == [n[1], n[3], n[2]]
|
||||
|
||||
tree.remove(n[1])
|
||||
assert len(tree._children) == 2
|
||||
assert tree._children[None] == [n[2]]
|
||||
assert tree._children[n[2]] == []
|
||||
assert tree._nodes == [n[2]]
|
||||
|
||||
|
||||
def test_siblings(tree_fixture):
|
||||
tree = tree_fixture[0]
|
||||
n = tree_fixture[1]
|
||||
tree.add(n[1])
|
||||
tree.add(n[2])
|
||||
tree.add(n[3])
|
||||
|
||||
assert tree.get_next_sibling(n[1]) is n[2]
|
||||
assert tree.get_next_sibling(n[2]) is n[3]
|
||||
try:
|
||||
tree.get_next_sibling(n[3])
|
||||
except IndexError:
|
||||
pass # Okay
|
||||
else:
|
||||
raise AssertionError(
|
||||
"Index should be out of range, not %s" % tree.get_next_sibling(n[3])
|
||||
)
|
||||
|
||||
assert tree.get_previous_sibling(n[3]) is n[2]
|
||||
assert tree.get_previous_sibling(n[2]) is n[1]
|
||||
try:
|
||||
tree.get_previous_sibling(n[1])
|
||||
except IndexError:
|
||||
pass # Okay
|
||||
else:
|
||||
raise AssertionError(
|
||||
"Index should be out of range, not %s" % tree.get_previous_sibling(n[1])
|
||||
)
|
||||
|
||||
|
||||
def test_reparent(tree_fixture):
|
||||
tree = tree_fixture[0]
|
||||
n = tree_fixture[1]
|
||||
tree.add(n[1])
|
||||
tree.add(n[2])
|
||||
tree.add(n[3])
|
||||
tree.add(n[4], parent=n[2])
|
||||
tree.add(n[5], parent=n[4])
|
||||
|
||||
assert tree.nodes == [n[1], n[2], n[4], n[5], n[3]], tree.nodes
|
||||
|
||||
tree.reparent(n[4], parent=n[1], index=0)
|
||||
assert tree.nodes == [n[1], n[4], n[5], n[2], n[3]], tree.nodes
|
||||
assert tree.get_children(n[2]) == [], tree.get_children(n[2])
|
||||
assert tree.get_children(n[1]) == [n[4]], tree.get_children(n[1])
|
||||
assert tree.get_children(n[4]) == [n[5]], tree.get_children(n[4])
|
||||
|
||||
tree.reparent(n[4], parent=None, index=0)
|
||||
assert tree.nodes == [n[4], n[5], n[1], n[2], n[3]], tree.nodes
|
||||
|
@ -1,103 +1,58 @@
|
||||
import unittest
|
||||
|
||||
from gaphas import state
|
||||
from gaphas.aspect import Connector, ConnectionSink
|
||||
from gaphas.canvas import Canvas
|
||||
from gaphas.examples import Box
|
||||
from gaphas.item import Line
|
||||
|
||||
state.observers.clear()
|
||||
state.subscribers.clear()
|
||||
|
||||
undo_list = []
|
||||
redo_list = []
|
||||
def test_undo_on_delete_element(revert_undo, undo_fixture):
|
||||
b1 = Box()
|
||||
b2 = Box()
|
||||
line = Line()
|
||||
|
||||
canvas = Canvas()
|
||||
canvas.add(b1)
|
||||
assert 2 == len(canvas.solver.constraints)
|
||||
|
||||
def undo_handler(event):
|
||||
undo_list.append(event)
|
||||
canvas.add(b2)
|
||||
assert 4 == len(canvas.solver.constraints)
|
||||
|
||||
canvas.add(line)
|
||||
|
||||
def undo():
|
||||
apply_me = list(undo_list)
|
||||
del undo_list[:]
|
||||
apply_me.reverse()
|
||||
for e in apply_me:
|
||||
state.saveapply(*e)
|
||||
redo_list[:] = undo_list[:]
|
||||
del undo_list[:]
|
||||
sink = ConnectionSink(b1, b1.ports()[0])
|
||||
connector = Connector(line, line.handles()[0])
|
||||
connector.connect(sink)
|
||||
|
||||
sink = ConnectionSink(b2, b2.ports()[0])
|
||||
connector = Connector(line, line.handles()[-1])
|
||||
connector.connect(sink)
|
||||
|
||||
class UndoTestCase(unittest.TestCase):
|
||||
def setUp(self):
|
||||
state.observers.add(state.revert_handler)
|
||||
state.subscribers.add(undo_handler)
|
||||
assert 6 == len(canvas.solver.constraints)
|
||||
assert 2 == len(list(canvas.get_connections(item=line)))
|
||||
|
||||
def shutDown(self):
|
||||
state.observers.remove(state.revert_handler)
|
||||
state.subscribers.remove(undo_handler)
|
||||
del undo_fixture[2][:] # Clear undo_list
|
||||
|
||||
def testUndoOnDeletedElement(self):
|
||||
b1 = Box()
|
||||
# Here disconnect is not invoked!
|
||||
canvas.remove(b2)
|
||||
|
||||
b2 = Box()
|
||||
line = Line()
|
||||
assert 3 == len(canvas.solver.constraints)
|
||||
assert 1 == len(list(canvas.get_connections(item=line)))
|
||||
|
||||
canvas = Canvas()
|
||||
canvas.add(b1)
|
||||
self.assertEqual(2, len(canvas.solver.constraints))
|
||||
cinfo = canvas.get_connection(line.handles()[0])
|
||||
assert b1 == cinfo.connected
|
||||
|
||||
canvas.add(b2)
|
||||
self.assertEqual(4, len(canvas.solver.constraints))
|
||||
cinfo = canvas.get_connection(line.handles()[-1])
|
||||
assert cinfo is None
|
||||
|
||||
canvas.add(line)
|
||||
assert [] == list(canvas.solver.constraints_with_variable(line.handles()[-1].pos.x))
|
||||
assert [] == list(canvas.solver.constraints_with_variable(line.handles()[-1].pos.y))
|
||||
|
||||
sink = ConnectionSink(b1, b1.ports()[0])
|
||||
connector = Connector(line, line.handles()[0])
|
||||
connector.connect(sink)
|
||||
undo_fixture[0]() # Call undo
|
||||
|
||||
sink = ConnectionSink(b2, b2.ports()[0])
|
||||
connector = Connector(line, line.handles()[-1])
|
||||
connector.connect(sink)
|
||||
assert 6 == len(canvas.solver.constraints)
|
||||
assert 2 == len(list(canvas.get_connections(item=line)))
|
||||
|
||||
self.assertEqual(6, len(canvas.solver.constraints))
|
||||
self.assertEqual(2, len(list(canvas.get_connections(item=line))))
|
||||
cinfo = canvas.get_connection(line.handles()[0])
|
||||
assert b1 == cinfo.connected
|
||||
|
||||
del undo_list[:]
|
||||
|
||||
# Here disconnect is not invoked!
|
||||
canvas.remove(b2)
|
||||
|
||||
self.assertEqual(3, len(canvas.solver.constraints))
|
||||
self.assertEqual(1, len(list(canvas.get_connections(item=line))))
|
||||
|
||||
cinfo = canvas.get_connection(line.handles()[0])
|
||||
self.assertEqual(b1, cinfo.connected)
|
||||
|
||||
cinfo = canvas.get_connection(line.handles()[-1])
|
||||
self.assertEqual(None, cinfo)
|
||||
|
||||
self.assertEqual(
|
||||
[], list(canvas.solver.constraints_with_variable(line.handles()[-1].pos.x))
|
||||
)
|
||||
self.assertEqual(
|
||||
[], list(canvas.solver.constraints_with_variable(line.handles()[-1].pos.y))
|
||||
)
|
||||
|
||||
undo()
|
||||
|
||||
self.assertEqual(6, len(canvas.solver.constraints))
|
||||
self.assertEqual(2, len(list(canvas.get_connections(item=line))))
|
||||
|
||||
cinfo = canvas.get_connection(line.handles()[0])
|
||||
self.assertEqual(b1, cinfo.connected)
|
||||
|
||||
cinfo = canvas.get_connection(line.handles()[-1])
|
||||
self.assertEqual(b2, cinfo.connected)
|
||||
|
||||
|
||||
# self.assertEqual(list(canvas.solver.constraints_with_variable(line.handles()[-1].pos.x)))
|
||||
# self.assertTrue(list(canvas.solver.constraints_with_variable(line.handles()[-1].pos.y)))
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
# vim:sw=4:et:ai
|
||||
cinfo = canvas.get_connection(line.handles()[-1])
|
||||
assert b2 == cinfo.connected
|
||||
|
@ -1,11 +1,11 @@
|
||||
"""
|
||||
Test cases for the View class.
|
||||
"""Test cases for the View class.
|
||||
|
||||
"""
|
||||
from __future__ import division
|
||||
|
||||
import math
|
||||
import unittest
|
||||
|
||||
import pytest
|
||||
from gi.repository import Gtk
|
||||
|
||||
from gaphas.canvas import Canvas
|
||||
@ -14,259 +14,229 @@ from gaphas.item import Line
|
||||
from gaphas.view import View, GtkView
|
||||
|
||||
|
||||
class ViewTestCase(unittest.TestCase):
|
||||
def test_bounding_box_calculations(self):
|
||||
"""
|
||||
A view created before and after the canvas is populated should contain
|
||||
the same data.
|
||||
"""
|
||||
canvas = Canvas()
|
||||
class ViewFixture(object):
|
||||
def __init__(self):
|
||||
self.canvas = Canvas()
|
||||
self.view = GtkView(self.canvas)
|
||||
self.window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
|
||||
self.window.add(self.view)
|
||||
self.window.show_all()
|
||||
|
||||
window1 = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
|
||||
view1 = GtkView(canvas=canvas)
|
||||
window1.add(view1)
|
||||
view1.realize()
|
||||
window1.show_all()
|
||||
|
||||
box = Box()
|
||||
box.matrix = (1.0, 0.0, 0.0, 1, 10, 10)
|
||||
canvas.add(box)
|
||||
|
||||
line = Line()
|
||||
line.fyzzyness = 1
|
||||
line.handles()[1].pos = (30, 30)
|
||||
# line.split_segment(0, 3)
|
||||
line.matrix.translate(30, 60)
|
||||
canvas.add(line)
|
||||
|
||||
window2 = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
|
||||
view2 = GtkView(canvas=canvas)
|
||||
window2.add(view2)
|
||||
window2.show_all()
|
||||
|
||||
# Process pending (expose) events, which cause the canvas to be drawn.
|
||||
while Gtk.events_pending():
|
||||
Gtk.main_iteration()
|
||||
|
||||
try:
|
||||
assert view2.get_item_bounding_box(box)
|
||||
assert view1.get_item_bounding_box(box)
|
||||
assert view1.get_item_bounding_box(box) == view2.get_item_bounding_box(
|
||||
box
|
||||
), (
|
||||
"%s != %s"
|
||||
% (view1.get_item_bounding_box(box), view2.get_item_bounding_box(box))
|
||||
)
|
||||
assert view1.get_item_bounding_box(line) == view2.get_item_bounding_box(
|
||||
line
|
||||
), (
|
||||
"%s != %s"
|
||||
% (view1.get_item_bounding_box(line), view2.get_item_bounding_box(line))
|
||||
)
|
||||
finally:
|
||||
window1.destroy()
|
||||
window2.destroy()
|
||||
|
||||
def test_get_item_at_point(self):
|
||||
"""
|
||||
Hover tool only reacts on motion-notify events
|
||||
"""
|
||||
canvas = Canvas()
|
||||
view = GtkView(canvas)
|
||||
window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
|
||||
window.add(view)
|
||||
window.show_all()
|
||||
|
||||
box = Box()
|
||||
canvas.add(box)
|
||||
self.box = Box()
|
||||
self.canvas.add(self.box)
|
||||
# No gtk main loop, so updates occur instantly
|
||||
assert not canvas.require_update()
|
||||
box.width = 50
|
||||
box.height = 50
|
||||
assert not self.canvas.require_update()
|
||||
|
||||
# Process pending (expose) events, which cause the canvas to be drawn.
|
||||
while Gtk.events_pending():
|
||||
Gtk.main_iteration()
|
||||
|
||||
assert len(view._qtree._ids) == 1
|
||||
assert not view._qtree._bucket.bounds == (
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
), view._qtree._bucket.bounds
|
||||
|
||||
assert view.get_item_at_point((10, 10)) is box
|
||||
assert view.get_item_at_point((60, 10)) is None
|
||||
|
||||
window.destroy()
|
||||
|
||||
def test_get_handle_at_point(self):
|
||||
canvas = Canvas()
|
||||
view = GtkView(canvas)
|
||||
window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
|
||||
window.add(view)
|
||||
window.show_all()
|
||||
|
||||
box = Box()
|
||||
box.min_width = 20
|
||||
box.min_height = 30
|
||||
box.matrix.translate(20, 20)
|
||||
box.matrix.rotate(math.pi / 1.5)
|
||||
canvas.add(box)
|
||||
|
||||
i, h = view.get_handle_at_point((20, 20))
|
||||
assert i is box
|
||||
assert h is box.handles()[0]
|
||||
|
||||
def test_get_handle_at_point_at_pi_div_2(self):
|
||||
canvas = Canvas()
|
||||
view = GtkView(canvas)
|
||||
window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
|
||||
window.add(view)
|
||||
window.show_all()
|
||||
|
||||
box = Box()
|
||||
box.min_width = 20
|
||||
box.min_height = 30
|
||||
box.matrix.translate(20, 20)
|
||||
box.matrix.rotate(math.pi / 2)
|
||||
canvas.add(box)
|
||||
|
||||
p = canvas.get_matrix_i2c(box).transform_point(0, 20)
|
||||
p = canvas.get_matrix_c2i(box).transform_point(20, 20)
|
||||
i, h = view.get_handle_at_point((20, 20))
|
||||
assert i is box
|
||||
assert h is box.handles()[0]
|
||||
|
||||
def test_item_removal(self):
|
||||
canvas = Canvas()
|
||||
view = GtkView(canvas)
|
||||
window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
|
||||
window.add(view)
|
||||
window.show_all()
|
||||
|
||||
box = Box()
|
||||
canvas.add(box)
|
||||
# No gtk main loop, so updates occur instantly
|
||||
assert not canvas.require_update()
|
||||
|
||||
# Process pending (expose) events, which cause the canvas to be drawn.
|
||||
while Gtk.events_pending():
|
||||
Gtk.main_iteration()
|
||||
|
||||
assert len(canvas.get_all_items()) == len(view._qtree)
|
||||
|
||||
view.focused_item = box
|
||||
canvas.remove(box)
|
||||
|
||||
assert len(canvas.get_all_items()) == 0
|
||||
assert len(view._qtree) == 0
|
||||
|
||||
window.destroy()
|
||||
|
||||
def test_view_registration(self):
|
||||
canvas = Canvas()
|
||||
|
||||
# Simple views do not register on the canvas
|
||||
|
||||
view = View(canvas)
|
||||
assert len(canvas._registered_views) == 0
|
||||
|
||||
box = Box()
|
||||
canvas.add(box)
|
||||
|
||||
# By default no complex updating/calculations are done:
|
||||
assert view not in box._matrix_i2v
|
||||
assert view not in box._matrix_v2i
|
||||
|
||||
# GTK view does register for updates though
|
||||
|
||||
view = GtkView(canvas)
|
||||
assert len(canvas._registered_views) == 1
|
||||
|
||||
# No entry, since GtkView is not realized and has no window
|
||||
assert view not in box._matrix_i2v
|
||||
assert view not in box._matrix_v2i
|
||||
|
||||
window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
|
||||
window.add(view)
|
||||
window.show_all()
|
||||
|
||||
# Now everything is realized and updated
|
||||
assert view in box._matrix_i2v
|
||||
assert view in box._matrix_v2i
|
||||
|
||||
view.canvas = None
|
||||
assert len(canvas._registered_views) == 0
|
||||
|
||||
assert view not in box._matrix_i2v
|
||||
assert view not in box._matrix_v2i
|
||||
|
||||
view.canvas = canvas
|
||||
assert len(canvas._registered_views) == 1
|
||||
|
||||
assert view in box._matrix_i2v
|
||||
assert view in box._matrix_v2i
|
||||
|
||||
def test_view_registration_2(self):
|
||||
"""
|
||||
Test view registration and destroy when view is destroyed.
|
||||
"""
|
||||
canvas = Canvas()
|
||||
view = GtkView(canvas)
|
||||
window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
|
||||
window.add(view)
|
||||
window.show_all()
|
||||
|
||||
box = Box()
|
||||
canvas.add(box)
|
||||
|
||||
assert hasattr(box, "_matrix_i2v")
|
||||
assert hasattr(box, "_matrix_v2i")
|
||||
|
||||
assert box._matrix_i2v[view]
|
||||
assert box._matrix_v2i[view]
|
||||
|
||||
assert len(canvas._registered_views) == 1
|
||||
assert view in canvas._registered_views
|
||||
|
||||
window.destroy()
|
||||
|
||||
assert len(canvas._registered_views) == 0
|
||||
|
||||
assert view not in box._matrix_i2v
|
||||
assert view not in box._matrix_v2i
|
||||
|
||||
def test_scroll_adjustments_signal(self):
|
||||
sc = Gtk.ScrolledWindow()
|
||||
view = GtkView(Canvas())
|
||||
sc.add(view)
|
||||
|
||||
assert view.hadjustment
|
||||
assert view.vadjustment
|
||||
assert view.hadjustment.get_value() == 0.0
|
||||
assert view.hadjustment.get_lower() == 0.0
|
||||
assert view.hadjustment.get_upper() == 1.0
|
||||
assert view.hadjustment.get_step_increment() == 0.0
|
||||
assert view.hadjustment.get_page_increment() == 1.0
|
||||
assert view.hadjustment.get_page_size() == 1.0
|
||||
assert view.vadjustment.get_value() == 0.0
|
||||
assert view.vadjustment.get_lower() == 0.0
|
||||
assert view.vadjustment.get_upper() == 1.0
|
||||
assert view.vadjustment.get_step_increment() == 0.0
|
||||
assert view.vadjustment.get_page_increment() == 1.0
|
||||
assert view.vadjustment.get_page_size() == 1.0
|
||||
|
||||
def test_scroll_adjustments(self):
|
||||
sc = Gtk.ScrolledWindow()
|
||||
view = GtkView(Canvas())
|
||||
sc.add(view)
|
||||
assert sc.get_hadjustment() is view.hadjustment
|
||||
assert sc.get_vadjustment() is view.vadjustment
|
||||
@pytest.fixture()
|
||||
def view_fixture():
|
||||
return ViewFixture()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
def test_bounding_box_calculations(view_fixture):
|
||||
"""A view created before and after the canvas is populated should contain
|
||||
the same data.
|
||||
|
||||
# vim:sw=4:et:ai
|
||||
"""
|
||||
view_fixture.view.realize()
|
||||
view_fixture.box.matrix = (1.0, 0.0, 0.0, 1, 10, 10)
|
||||
|
||||
line = Line()
|
||||
line.fuzziness = 1
|
||||
line.handles()[1].pos = (30, 30)
|
||||
line.matrix.translate(30, 60)
|
||||
view_fixture.canvas.add(line)
|
||||
|
||||
window2 = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
|
||||
view2 = GtkView(canvas=view_fixture.canvas)
|
||||
window2.add(view2)
|
||||
window2.show_all()
|
||||
|
||||
# Process pending (expose) events, which cause the canvas to be drawn.
|
||||
while Gtk.events_pending():
|
||||
Gtk.main_iteration()
|
||||
|
||||
try:
|
||||
assert view2.get_item_bounding_box(view_fixture.box)
|
||||
assert view_fixture.view.get_item_bounding_box(view_fixture.box)
|
||||
assert view_fixture.view.get_item_bounding_box(
|
||||
view_fixture.box
|
||||
) == view2.get_item_bounding_box(view_fixture.box), (
|
||||
"%s != %s"
|
||||
% (
|
||||
view_fixture.view.get_item_bounding_box(view_fixture.box),
|
||||
view2.get_item_bounding_box(view_fixture.box),
|
||||
)
|
||||
)
|
||||
assert view_fixture.view.get_item_bounding_box(
|
||||
line
|
||||
) == view2.get_item_bounding_box(line), (
|
||||
"%s != %s"
|
||||
% (
|
||||
view_fixture.view.get_item_bounding_box(line),
|
||||
view2.get_item_bounding_box(line),
|
||||
)
|
||||
)
|
||||
finally:
|
||||
view_fixture.window.destroy()
|
||||
window2.destroy()
|
||||
|
||||
|
||||
def test_get_item_at_point(view_fixture):
|
||||
"""Hover tool only reacts on motion-notify events.
|
||||
|
||||
"""
|
||||
view_fixture.box.width = 50
|
||||
view_fixture.box.height = 50
|
||||
assert len(view_fixture.view._qtree._ids) == 1
|
||||
assert not view_fixture.view._qtree._bucket.bounds == (
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
), view_fixture.view._qtree._bucket.bounds
|
||||
|
||||
assert view_fixture.view.get_item_at_point((10, 10)) is view_fixture.box
|
||||
assert view_fixture.view.get_item_at_point((60, 10)) is None
|
||||
|
||||
view_fixture.window.destroy()
|
||||
|
||||
|
||||
def test_get_handle_at_point(view_fixture):
|
||||
box = Box()
|
||||
box.min_width = 20
|
||||
box.min_height = 30
|
||||
box.matrix.translate(20, 20)
|
||||
box.matrix.rotate(math.pi / 1.5)
|
||||
view_fixture.canvas.add(box)
|
||||
|
||||
i, h = view_fixture.view.get_handle_at_point((20, 20))
|
||||
assert i is box
|
||||
assert h is box.handles()[0]
|
||||
|
||||
|
||||
def test_get_handle_at_point_at_pi_div_2(view_fixture):
|
||||
box = Box()
|
||||
box.min_width = 20
|
||||
box.min_height = 30
|
||||
box.matrix.translate(20, 20)
|
||||
box.matrix.rotate(math.pi / 2)
|
||||
view_fixture.canvas.add(box)
|
||||
box.matrix.translate(20, 20)
|
||||
box.matrix.rotate(math.pi / 2)
|
||||
|
||||
i, h = view_fixture.view.get_handle_at_point((20, 20))
|
||||
assert i is box
|
||||
assert h is box.handles()[0]
|
||||
|
||||
|
||||
def test_item_removal(view_fixture):
|
||||
assert len(view_fixture.canvas.get_all_items()) == len(view_fixture.view._qtree)
|
||||
|
||||
view_fixture.view.focused_item = view_fixture.box
|
||||
view_fixture.canvas.remove(view_fixture.box)
|
||||
|
||||
assert len(view_fixture.canvas.get_all_items()) == 0
|
||||
assert len(view_fixture.view._qtree) == 0
|
||||
|
||||
view_fixture.window.destroy()
|
||||
|
||||
|
||||
def test_view_registration(view_fixture):
|
||||
canvas = Canvas()
|
||||
|
||||
# Simple views do not register on the canvas
|
||||
|
||||
view = View(canvas)
|
||||
assert len(canvas._registered_views) == 0
|
||||
|
||||
box = Box()
|
||||
canvas.add(box)
|
||||
|
||||
# By default no complex updating/calculations are done:
|
||||
assert view not in box._matrix_i2v
|
||||
assert view not in box._matrix_v2i
|
||||
|
||||
# GTK view does register for updates though
|
||||
|
||||
view = GtkView(canvas)
|
||||
assert len(canvas._registered_views) == 1
|
||||
|
||||
# No entry, since GtkView is not realized and has no window
|
||||
assert view not in box._matrix_i2v
|
||||
assert view not in box._matrix_v2i
|
||||
|
||||
window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL)
|
||||
window.add(view)
|
||||
window.show_all()
|
||||
|
||||
# Now everything is realized and updated
|
||||
assert view in box._matrix_i2v
|
||||
assert view in box._matrix_v2i
|
||||
|
||||
view.canvas = None
|
||||
assert len(canvas._registered_views) == 0
|
||||
|
||||
assert view not in box._matrix_i2v
|
||||
assert view not in box._matrix_v2i
|
||||
|
||||
view.canvas = canvas
|
||||
assert len(canvas._registered_views) == 1
|
||||
|
||||
assert view in box._matrix_i2v
|
||||
assert view in box._matrix_v2i
|
||||
|
||||
|
||||
def test_view_registration_2(view_fixture):
|
||||
"""Test view registration and destroy when view is destroyed.
|
||||
|
||||
"""
|
||||
assert hasattr(view_fixture.box, "_matrix_i2v")
|
||||
assert hasattr(view_fixture.box, "_matrix_v2i")
|
||||
|
||||
assert view_fixture.box._matrix_i2v[view_fixture.view]
|
||||
assert view_fixture.box._matrix_v2i[view_fixture.view]
|
||||
|
||||
assert len(view_fixture.canvas._registered_views) == 1
|
||||
assert view_fixture.view in view_fixture.canvas._registered_views
|
||||
|
||||
view_fixture.window.destroy()
|
||||
|
||||
assert len(view_fixture.canvas._registered_views) == 0
|
||||
|
||||
assert view_fixture.view not in view_fixture.box._matrix_i2v
|
||||
assert view_fixture.view not in view_fixture.box._matrix_v2i
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def sc_view():
|
||||
sc = Gtk.ScrolledWindow()
|
||||
view = GtkView(Canvas())
|
||||
sc.add(view)
|
||||
return view, sc
|
||||
|
||||
|
||||
def test_scroll_adjustments_signal(sc_view):
|
||||
assert sc_view[0].hadjustment
|
||||
assert sc_view[0].vadjustment
|
||||
assert sc_view[0].hadjustment.get_value() == 0.0
|
||||
assert sc_view[0].hadjustment.get_lower() == 0.0
|
||||
assert sc_view[0].hadjustment.get_upper() == 1.0
|
||||
assert sc_view[0].hadjustment.get_step_increment() == 0.0
|
||||
assert sc_view[0].hadjustment.get_page_increment() == 1.0
|
||||
assert sc_view[0].hadjustment.get_page_size() == 1.0
|
||||
assert sc_view[0].vadjustment.get_value() == 0.0
|
||||
assert sc_view[0].vadjustment.get_lower() == 0.0
|
||||
assert sc_view[0].vadjustment.get_upper() == 1.0
|
||||
assert sc_view[0].vadjustment.get_step_increment() == 0.0
|
||||
assert sc_view[0].vadjustment.get_page_increment() == 1.0
|
||||
assert sc_view[0].vadjustment.get_page_size() == 1.0
|
||||
|
||||
|
||||
def test_scroll_adjustments(sc_view):
|
||||
assert sc_view[1].get_hadjustment() is sc_view[0].hadjustment
|
||||
assert sc_view[1].get_vadjustment() is sc_view[0].vadjustment
|
||||
|
@ -252,7 +252,7 @@ class View(object):
|
||||
if -d < (hx - x) < d and -d < (hy - y) < d:
|
||||
return h
|
||||
|
||||
# The focused item is the prefered item for handle grabbing
|
||||
# The focused item is the preferred item for handle grabbing
|
||||
if self.focused_item:
|
||||
h = find(self.focused_item)
|
||||
if h:
|
||||
|
Loading…
x
Reference in New Issue
Block a user