426 lines
15 KiB
Python
426 lines
15 KiB
Python
'''
|
|
Bubble unit tests
|
|
=================
|
|
author: Anthony Zimmermann (Anthony.Zimmermann@protonmail.com)
|
|
'''
|
|
|
|
import pytest
|
|
from kivy.tests.common import GraphicUnitTest
|
|
|
|
from kivy.base import EventLoop
|
|
|
|
from kivy.uix.bubble import Bubble
|
|
from kivy.uix.bubble import BubbleContent
|
|
from kivy.uix.bubble import BubbleButton
|
|
|
|
|
|
class _TestBubbleButton(BubbleButton):
|
|
|
|
def __init__(self, button_size=(None, None), *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
size_x, size_y = button_size
|
|
if size_x is not None:
|
|
self.size_hint_x = None
|
|
self.width = size_x
|
|
if size_y is not None:
|
|
self.size_hint_y = None
|
|
self.height = size_y
|
|
|
|
|
|
class _TestBubbleContent(BubbleContent):
|
|
|
|
def update_size(self, instance, value):
|
|
self.size = self.minimum_size
|
|
|
|
def __init__(self, *args, **kwargs):
|
|
super().__init__(*args, **kwargs)
|
|
|
|
self.bind(minimum_size=self.update_size)
|
|
|
|
|
|
class _TestBubble(Bubble):
|
|
|
|
@property
|
|
def arrow_length(self):
|
|
return self._arrow_image.height
|
|
|
|
@property
|
|
def arrow_width(self):
|
|
return self._arrow_image.width
|
|
|
|
@property
|
|
def arrow_rotation(self):
|
|
return self._arrow_image_scatter.rotation
|
|
|
|
@property
|
|
def arrow_layout_pos(self):
|
|
return self._arrow_image_layout.pos
|
|
|
|
@property
|
|
def arrow_layout_size(self):
|
|
return self._arrow_image_layout.size
|
|
|
|
@property
|
|
def arrow_center_pos_within_arrow_layout(self):
|
|
x = self._arrow_image_scatter_wrapper.center_x
|
|
y = self._arrow_image_scatter_wrapper.center_y
|
|
return x, y
|
|
|
|
|
|
# (bubble_width, button_height, arrow_pos)
|
|
bubble_layout_with_predefined_arrow_pos_test_params = [
|
|
(158.9, 34.3, "bottom_left"), # noqa: E201,E241
|
|
(651.4, 26.1, "bottom_mid"), # noqa: E201,E241
|
|
( 6.5, 44.7, "bottom_right"), # noqa: E201,E241
|
|
(754.6, 50.6, "top_left"), # noqa: E201,E241
|
|
(957.8, 74.1, "top_mid"), # noqa: E201,E241
|
|
(852.1, 33.1, "top_right"), # noqa: E201,E241
|
|
(472.9, 45.1, "left_top"), # noqa: E201,E241
|
|
(578.3, 52.7, "left_mid"), # noqa: E201,E241
|
|
(687.8, 17.7, "left_bottom"), # noqa: E201,E241
|
|
(313.7, 8.6, "right_top"), # noqa: E201,E241
|
|
(194.3, 46.4, "right_mid"), # noqa: E201,E241
|
|
( 59.3, 29.7, "right_bottom"), # noqa: E201,E241
|
|
]
|
|
|
|
bubble_layout_with_flex_arrow_pos_test_params = [
|
|
(101.3, 346.0, 0.0, 73.6, "l"), # noqa: E201,E241
|
|
(489.0, 535.1, 0.0, 442.1, "l"), # noqa: E201,E241
|
|
(390.9, 728.1, 0.0, 114.3, "l"), # noqa: E201,E241
|
|
(224.5, 675.5, 0.0, 560.6, "l"), # noqa: E201,E241
|
|
(264.9, 677.3, 0.0, 8.3, "l"), # noqa: E201,E241
|
|
(544.6, 126.0, 0.0, 120.9, "l"), # noqa: E201,E241
|
|
(290.9, 962.6, 0.0, 275.5, "l"), # noqa: E201,E241
|
|
(358.4, 514.4, 0.0, 427.1, "l"), # noqa: E201,E241
|
|
(604.3, 648.2, 0.0, 226.1, "l"), # noqa: E201,E241
|
|
(287.4, 875.6, 0.0, 446.5, "l"), # noqa: E201,E241
|
|
(755.7, 103.5, 444.6, 0.0, "b"), # noqa: E201,E241
|
|
(307.9, 471.7, 80.9, 0.0, "b"), # noqa: E201,E241
|
|
(849.9, 194.8, 652.7, 0.0, "b"), # noqa: E201,E241
|
|
(975.7, 691.0, 120.9, 0.0, "b"), # noqa: E201,E241
|
|
(539.1, 903.3, 530.6, 0.0, "b"), # noqa: E201,E241
|
|
( 37.5, 727.5, 37.0, 0.0, "b"), # noqa: E201,E241
|
|
(856.5, 779.0, 565.5, 0.0, "b"), # noqa: E201,E241
|
|
(536.7, 228.3, 48.4, 0.0, "b"), # noqa: E201,E241
|
|
(170.9, 870.4, 127.6, 0.0, "b"), # noqa: E201,E241
|
|
(955.7, 530.6, 526.0, 0.0, "b"), # noqa: E201,E241
|
|
(878.1, 690.4, 878.1, 18.8, "r"), # noqa: E201,E241
|
|
(771.6, 365.2, 771.6, 31.1, "r"), # noqa: E201,E241
|
|
(679.7, 305.4, 679.7, 259.6, "r"), # noqa: E201,E241
|
|
(700.2, 614.6, 700.2, 105.4, "r"), # noqa: E201,E241
|
|
(444.1, 864.5, 444.1, 152.3, "r"), # noqa: E201,E241
|
|
(189.0, 790.4, 189.0, 602.9, "r"), # noqa: E201,E241
|
|
(376.0, 993.9, 376.0, 486.4, "r"), # noqa: E201,E241
|
|
(518.5, 338.5, 518.5, 194.6, "r"), # noqa: E201,E241
|
|
(982.1, 666.1, 982.1, 282.5, "r"), # noqa: E201,E241
|
|
(926.4, 565.1, 926.4, 187.3, "r"), # noqa: E201,E241
|
|
(375.2, 746.6, 36.2, 746.6, "t"), # noqa: E201,E241
|
|
(448.9, 228.5, 297.4, 228.5, "t"), # noqa: E201,E241
|
|
(792.3, 593.5, 746.2, 593.5, "t"), # noqa: E201,E241
|
|
(856.1, 89.7, 23.1, 89.7, "t"), # noqa: E201,E241
|
|
(721.3, 319.0, 356.5, 319.0, "t"), # noqa: E201,E241
|
|
(127.7, 355.7, 69.3, 355.7, "t"), # noqa: E201,E241
|
|
(412.3, 493.8, 163.2, 493.8, "t"), # noqa: E201,E241
|
|
( 40.8, 115.8, 15.8, 115.8, "t"), # noqa: E201,E241
|
|
(233.9, 148.5, 189.4, 148.5, "t"), # noqa: E201,E241
|
|
(982.4, 661.5, 105.9, 661.5, "t"), # noqa: E201,E241
|
|
]
|
|
|
|
|
|
class BubbleTest(GraphicUnitTest):
|
|
|
|
def move_frames(self, t):
|
|
for i in range(t):
|
|
EventLoop.idle()
|
|
|
|
def test_no_content(self):
|
|
bubble = Bubble()
|
|
self.render(bubble)
|
|
|
|
def test_add_remove_content(self):
|
|
bubble = Bubble()
|
|
content = BubbleContent()
|
|
bubble.add_widget(content)
|
|
self.render(bubble)
|
|
|
|
bubble.remove_widget(content)
|
|
self.render(bubble)
|
|
|
|
def test_add_arbitrary_content(self):
|
|
from kivy.uix.gridlayout import GridLayout
|
|
bubble = Bubble()
|
|
content = GridLayout()
|
|
bubble.add_widget(content)
|
|
self.render(bubble)
|
|
|
|
def test_add_two_content_widgets_fails(self):
|
|
from kivy.uix.bubble import BubbleException
|
|
bubble = Bubble()
|
|
content_1 = BubbleContent()
|
|
content_2 = BubbleContent()
|
|
bubble.add_widget(content_1)
|
|
with self.assertRaises(BubbleException):
|
|
bubble.add_widget(content_2)
|
|
|
|
def test_add_content_with_buttons(self):
|
|
bubble = Bubble()
|
|
|
|
content = BubbleContent()
|
|
content.add_widget(BubbleButton(text="Option A"))
|
|
content.add_widget(BubbleButton(text="Option B"))
|
|
|
|
bubble.add_widget(content)
|
|
self.render(bubble)
|
|
|
|
def assertSequenceAlmostEqual(self, seq1, seq2, delta=None):
|
|
assert len(seq1) == len(seq2)
|
|
for a, b in zip(seq1, seq2):
|
|
self.assertAlmostEqual(a, b, delta=delta)
|
|
|
|
def assertTestBubbleLayoutWithPredefinedArrowPos(self, bubble):
|
|
arrow_length = bubble.arrow_length
|
|
arrow_width = bubble.arrow_width
|
|
bubble_width = bubble.test_bubble_width
|
|
button_height = bubble.test_button_height
|
|
|
|
# assert content size
|
|
expected_content_size = {
|
|
"bottom_left": (bubble_width, button_height),
|
|
"bottom_mid": (bubble_width, button_height),
|
|
"bottom_right": (bubble_width, button_height),
|
|
"top_left": (bubble_width, button_height),
|
|
"top_mid": (bubble_width, button_height),
|
|
"top_right": (bubble_width, button_height),
|
|
"left_top": (bubble_width - arrow_length, button_height),
|
|
"left_mid": (bubble_width - arrow_length, button_height),
|
|
"left_bottom": (bubble_width - arrow_length, button_height),
|
|
"right_top": (bubble_width - arrow_length, button_height),
|
|
"right_mid": (bubble_width - arrow_length, button_height),
|
|
"right_bottom": (bubble_width - arrow_length, button_height),
|
|
}[bubble.arrow_pos]
|
|
self.assertSequenceAlmostEqual(
|
|
bubble.content.size,
|
|
expected_content_size,
|
|
)
|
|
|
|
# assert arrow layout size
|
|
expected_arrow_layout_size = {
|
|
"bottom_left": (bubble_width, arrow_length),
|
|
"bottom_mid": (bubble_width, arrow_length),
|
|
"bottom_right": (bubble_width, arrow_length),
|
|
"top_left": (bubble_width, arrow_length),
|
|
"top_mid": (bubble_width, arrow_length),
|
|
"top_right": (bubble_width, arrow_length),
|
|
"left_top": (arrow_length, button_height),
|
|
"left_mid": (arrow_length, button_height),
|
|
"left_bottom": (arrow_length, button_height),
|
|
"right_top": (arrow_length, button_height),
|
|
"right_mid": (arrow_length, button_height),
|
|
"right_bottom": (arrow_length, button_height),
|
|
}[bubble.arrow_pos]
|
|
self.assertSequenceAlmostEqual(
|
|
bubble.arrow_layout_size,
|
|
expected_arrow_layout_size,
|
|
)
|
|
|
|
# assert content position
|
|
expected_content_position = {
|
|
"bottom_left": (0, arrow_length),
|
|
"bottom_mid": (0, arrow_length),
|
|
"bottom_right": (0, arrow_length),
|
|
"top_left": (0, 0),
|
|
"top_mid": (0, 0),
|
|
"top_right": (0, 0),
|
|
"left_top": (arrow_length, 0),
|
|
"left_mid": (arrow_length, 0),
|
|
"left_bottom": (arrow_length, 0),
|
|
"right_top": (0, 0),
|
|
"right_mid": (0, 0),
|
|
"right_bottom": (0, 0),
|
|
}[bubble.arrow_pos]
|
|
self.assertSequenceAlmostEqual(
|
|
bubble.content.pos,
|
|
expected_content_position,
|
|
)
|
|
|
|
# assert arrow layout position
|
|
expected_arrow_layout_position = {
|
|
"bottom_left": (0, 0),
|
|
"bottom_mid": (0, 0),
|
|
"bottom_right": (0, 0),
|
|
"top_left": (0, button_height),
|
|
"top_mid": (0, button_height),
|
|
"top_right": (0, button_height),
|
|
"left_top": (0, 0),
|
|
"left_mid": (0, 0),
|
|
"left_bottom": (0, 0),
|
|
"right_top": (bubble_width - arrow_length, 0),
|
|
"right_mid": (bubble_width - arrow_length, 0),
|
|
"right_bottom": (bubble_width - arrow_length, 0),
|
|
}[bubble.arrow_pos]
|
|
self.assertSequenceAlmostEqual(
|
|
bubble.arrow_layout_pos,
|
|
expected_arrow_layout_position,
|
|
)
|
|
|
|
# assert arrow position within arrow layout
|
|
hal = arrow_length / 2 # hal := half arrow length
|
|
x_offset = 0.05 * bubble_width
|
|
y_offset = 0.05 * button_height
|
|
expected_arrow_center_pos_within_arrow_layout = {
|
|
"bottom_left": (x_offset + arrow_width / 2, hal),
|
|
"bottom_mid": (bubble_width / 2, hal),
|
|
"bottom_right": (bubble_width - arrow_width / 2 - x_offset, hal),
|
|
"top_left": (x_offset + arrow_width / 2, hal),
|
|
"top_mid": (bubble_width / 2, hal),
|
|
"top_right": (bubble_width - arrow_width / 2 - x_offset, hal),
|
|
"left_top": (hal, button_height - arrow_width / 2 - y_offset),
|
|
"left_mid": (hal, button_height / 2),
|
|
"left_bottom": (hal, y_offset + arrow_width / 2),
|
|
"right_top": (hal, button_height - arrow_width / 2 - y_offset),
|
|
"right_mid": (hal, button_height / 2),
|
|
"right_bottom": (hal, y_offset + arrow_width / 2),
|
|
}[bubble.arrow_pos]
|
|
self.assertSequenceAlmostEqual(
|
|
bubble.arrow_center_pos_within_arrow_layout,
|
|
expected_arrow_center_pos_within_arrow_layout,
|
|
)
|
|
|
|
# assert arrow rotation
|
|
expected_arrow_rotation = {
|
|
"bottom_left": 0,
|
|
"bottom_mid": 0,
|
|
"bottom_right": 0,
|
|
"top_left": 180,
|
|
"top_mid": 180,
|
|
"top_right": 180,
|
|
"left_top": 270,
|
|
"left_mid": 270,
|
|
"left_bottom": 270,
|
|
"right_top": 90,
|
|
"right_mid": 90,
|
|
"right_bottom": 90,
|
|
}[bubble.arrow_pos]
|
|
self.assertAlmostEqual(
|
|
bubble.arrow_rotation,
|
|
expected_arrow_rotation,
|
|
)
|
|
|
|
def test_bubble_layout_with_predefined_arrow_pos(self):
|
|
|
|
for params in bubble_layout_with_predefined_arrow_pos_test_params:
|
|
bubble_width, button_height, arrow_pos = params
|
|
|
|
with self.subTest():
|
|
print(
|
|
"(bubble_width={}, button_height={}, arrow_pos={})".format(
|
|
*params
|
|
)
|
|
)
|
|
bubble = _TestBubble(arrow_pos=arrow_pos)
|
|
bubble.size_hint = (None, None)
|
|
bubble.test_bubble_width = bubble_width
|
|
bubble.test_button_height = button_height
|
|
|
|
def update_bubble_size(instance, value):
|
|
w = bubble_width
|
|
h = bubble.content_height + bubble.arrow_margin_y
|
|
bubble.size = (w, h)
|
|
bubble.bind(
|
|
content_size=update_bubble_size,
|
|
arrow_margin=update_bubble_size,
|
|
)
|
|
|
|
content = _TestBubbleContent()
|
|
for i in range(3):
|
|
content.add_widget(
|
|
_TestBubbleButton(
|
|
button_size=(None, button_height),
|
|
text="Option {}".format(i)
|
|
)
|
|
)
|
|
|
|
bubble.add_widget(content)
|
|
self.render(bubble)
|
|
|
|
self.assertTestBubbleLayoutWithPredefinedArrowPos(bubble)
|
|
|
|
def test_bubble_layout_without_arrow(self):
|
|
bubble_width = 200
|
|
button_height = 30
|
|
|
|
bubble = _TestBubble(show_arrow=False)
|
|
bubble.size_hint = (None, None)
|
|
|
|
def update_bubble_size(instance, value):
|
|
w = bubble_width
|
|
h = bubble.content_height
|
|
bubble.size = (w, h)
|
|
bubble.bind(content_size=update_bubble_size)
|
|
|
|
content = _TestBubbleContent(orientation="vertical")
|
|
for i in range(7):
|
|
content.add_widget(
|
|
_TestBubbleButton(
|
|
button_size=(None, button_height),
|
|
text="Option_{}".format(i)
|
|
)
|
|
)
|
|
|
|
bubble.add_widget(content)
|
|
self.render(bubble)
|
|
|
|
# assert content size
|
|
self.assertSequenceAlmostEqual(
|
|
bubble.content.size,
|
|
(bubble_width, 7 * button_height),
|
|
)
|
|
|
|
# assert content position
|
|
self.assertSequenceAlmostEqual(
|
|
bubble.content.pos,
|
|
(0, 0),
|
|
)
|
|
|
|
def test_bubble_layout_with_flex_arrow_pos(self):
|
|
for params in bubble_layout_with_flex_arrow_pos_test_params:
|
|
bubble_size = params[:2]
|
|
flex_arrow_pos = params[2:4]
|
|
arrow_side = params[4]
|
|
with self.subTest():
|
|
print("(w={}, h={}, x={}, y={}, side={})".format(*params))
|
|
|
|
bubble = _TestBubble()
|
|
bubble.size_hint = (None, None)
|
|
bubble.size = bubble_size
|
|
bubble.flex_arrow_pos = flex_arrow_pos
|
|
|
|
content = _TestBubbleContent(orientation="vertical")
|
|
content.size_hint = (1, 1)
|
|
|
|
button = _TestBubbleButton(
|
|
button_size=(None, None),
|
|
text="Option",
|
|
)
|
|
button.size_hint_y = 1
|
|
|
|
content.add_widget(button)
|
|
bubble.add_widget(content)
|
|
|
|
self.render(bubble)
|
|
|
|
haw = bubble.arrow_width / 2 # half arrow_width
|
|
if arrow_side in ["l", "r"]:
|
|
self.assertSequenceAlmostEqual(
|
|
bubble.arrow_center_pos_within_arrow_layout,
|
|
(haw, flex_arrow_pos[1]),
|
|
delta=haw,
|
|
)
|
|
elif arrow_side in ["b", "t"]:
|
|
self.assertSequenceAlmostEqual(
|
|
bubble.arrow_center_pos_within_arrow_layout,
|
|
(flex_arrow_pos[0], haw),
|
|
delta=haw,
|
|
)
|