293 lines
8.0 KiB
Python
293 lines
8.0 KiB
Python
|
import pytest
|
||
|
from string import ascii_letters
|
||
|
from random import randint
|
||
|
import gc
|
||
|
|
||
|
import sys
|
||
|
|
||
|
|
||
|
@pytest.fixture
|
||
|
def kivy_benchmark(benchmark, kivy_clock):
|
||
|
from kivy.core.window import Window
|
||
|
from kivy.cache import Cache
|
||
|
from kivy.utils import platform
|
||
|
import kivy
|
||
|
from kivy.core.gl import glGetString, GL_VENDOR, GL_RENDERER, GL_VERSION
|
||
|
from kivy.context import Context
|
||
|
from kivy.clock import ClockBase
|
||
|
from kivy.factory import FactoryBase, Factory
|
||
|
from kivy.lang.builder import BuilderBase, Builder
|
||
|
|
||
|
context = Context(init=False)
|
||
|
context['Clock'] = ClockBase()
|
||
|
context['Factory'] = FactoryBase.create_from(Factory)
|
||
|
context['Builder'] = BuilderBase.create_from(Builder)
|
||
|
|
||
|
for category in list(Cache._objects.keys()):
|
||
|
if category not in Cache._categories:
|
||
|
continue
|
||
|
|
||
|
for key in list(Cache._objects[category].keys()):
|
||
|
Cache.remove(category, key)
|
||
|
|
||
|
gc.collect()
|
||
|
|
||
|
benchmark.extra_info['platform'] = str(sys.platform)
|
||
|
benchmark.extra_info['python_version'] = str(sys.version)
|
||
|
benchmark.extra_info['python_api'] = str(sys.api_version)
|
||
|
benchmark.extra_info['kivy_platform'] = platform
|
||
|
benchmark.extra_info['kivy_version'] = kivy.__version__
|
||
|
benchmark.extra_info['gl_vendor'] = str(glGetString(GL_VENDOR))
|
||
|
benchmark.extra_info['gl_renderer'] = str(glGetString(GL_RENDERER))
|
||
|
benchmark.extra_info['gl_version'] = str(glGetString(GL_VERSION))
|
||
|
|
||
|
context.push()
|
||
|
try:
|
||
|
yield benchmark
|
||
|
finally:
|
||
|
context.pop()
|
||
|
|
||
|
|
||
|
def test_event_dispatcher_creation(kivy_benchmark):
|
||
|
from kivy.event import EventDispatcher
|
||
|
|
||
|
class Event(EventDispatcher):
|
||
|
pass
|
||
|
# create one just so we don't incur loading cost
|
||
|
e = Event()
|
||
|
kivy_benchmark(Event)
|
||
|
|
||
|
|
||
|
def test_widget_creation(kivy_benchmark):
|
||
|
from kivy.uix.widget import Widget
|
||
|
# create one just so we don't incur loading cost
|
||
|
w = Widget()
|
||
|
kivy_benchmark(Widget)
|
||
|
|
||
|
|
||
|
def test_kv_widget_creation(kivy_benchmark):
|
||
|
from kivy.lang import Builder
|
||
|
from kivy.uix.widget import Widget
|
||
|
|
||
|
class MyWidget(Widget):
|
||
|
pass
|
||
|
|
||
|
Builder.load_string("""
|
||
|
<MyWidget>:
|
||
|
width: 55
|
||
|
height: 37
|
||
|
x: self.width + 5
|
||
|
y: self.height + 32
|
||
|
""")
|
||
|
|
||
|
# create one just so we don't incur loading cost
|
||
|
w = MyWidget()
|
||
|
kivy_benchmark(MyWidget)
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize('test_component', ['create', 'set'])
|
||
|
def test_complex_kv_widget(kivy_benchmark, test_component):
|
||
|
from kivy.lang import Builder
|
||
|
from kivy.uix.widget import Widget
|
||
|
|
||
|
class MyWidget(Widget):
|
||
|
pass
|
||
|
|
||
|
Builder.load_string("""
|
||
|
<MyWidget>:
|
||
|
width: 1
|
||
|
height: '{}dp'.format(self.width + 1)
|
||
|
x: self.height + 1
|
||
|
y: self.x + 1
|
||
|
size_hint_min: self.size_hint
|
||
|
size_hint_max_y: self.size_hint_min_y
|
||
|
size_hint_max_x: self.size_hint_min_x
|
||
|
opacity: sum(self.size_hint_min) + sum(self.size_hint_max)
|
||
|
""")
|
||
|
|
||
|
# create one just so we don't incur loading cost
|
||
|
widget = MyWidget()
|
||
|
w = 0
|
||
|
sh = 0
|
||
|
|
||
|
def set_value():
|
||
|
nonlocal w, sh
|
||
|
w += 1
|
||
|
sh += 1
|
||
|
widget.width = w
|
||
|
widget.size_hint = sh, sh
|
||
|
|
||
|
if test_component == 'create':
|
||
|
kivy_benchmark(MyWidget)
|
||
|
else:
|
||
|
kivy_benchmark(set_value)
|
||
|
|
||
|
|
||
|
def get_event_class(name, args, kwargs):
|
||
|
from kivy.event import EventDispatcher
|
||
|
import kivy.properties
|
||
|
from kivy.properties import BooleanProperty, ReferenceListProperty, \
|
||
|
AliasProperty
|
||
|
|
||
|
if name == 'AliasProperty':
|
||
|
class Event(EventDispatcher):
|
||
|
def get_a(self):
|
||
|
return 0
|
||
|
|
||
|
def set_a(self, value):
|
||
|
pass
|
||
|
a = AliasProperty(get_a, set_a)
|
||
|
|
||
|
elif name == 'ReferenceListProperty':
|
||
|
class Event(EventDispatcher):
|
||
|
|
||
|
a1 = BooleanProperty(0)
|
||
|
a2 = BooleanProperty(0)
|
||
|
a = ReferenceListProperty(a1, a2)
|
||
|
|
||
|
else:
|
||
|
cls = getattr(kivy.properties, name)
|
||
|
|
||
|
class Event(EventDispatcher):
|
||
|
|
||
|
a = cls(*args, **kwargs)
|
||
|
|
||
|
return Event
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize('name,args,kwargs', [
|
||
|
('NumericProperty', (0,), {}),
|
||
|
('ObjectProperty', (None,), {}),
|
||
|
('VariableListProperty', ([0, 0, 0, 0],), {}),
|
||
|
('BoundedNumericProperty', (1, ), {'min': 0, 'max': 2}),
|
||
|
('DictProperty', ({}, ), {}),
|
||
|
('ColorProperty', ([1, 1, 1, 1],), {}),
|
||
|
('BooleanProperty', (False,), {}),
|
||
|
('OptionProperty', ('a',), {'options': ['a', 'b']}),
|
||
|
('StringProperty', ('',), {}),
|
||
|
('ListProperty', ([],), {}),
|
||
|
('AliasProperty', (), {}),
|
||
|
('ReferenceListProperty', (), {}),
|
||
|
])
|
||
|
def test_property_creation(kivy_benchmark, name, args, kwargs):
|
||
|
event_cls = get_event_class(name, args, kwargs)
|
||
|
|
||
|
# create one just so we don't incur loading cost
|
||
|
e = event_cls()
|
||
|
kivy_benchmark(event_cls)
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize('name,args,kwargs,val,reset_val', [
|
||
|
('NumericProperty', (0,), {}, 10, 0),
|
||
|
('NumericProperty', (0,), {}, '10dp', 0),
|
||
|
('NumericProperty', (0,), {}, [10, 'dp'], 0),
|
||
|
('ObjectProperty', (None,), {}, 5, 0),
|
||
|
('VariableListProperty', ([0, 0, 0, 0],), {}, [2, 4], [0]),
|
||
|
('BoundedNumericProperty', (1, ), {'min': 0, 'max': 2}, .5, 1),
|
||
|
('DictProperty', ({}, ), {}, {'name': 1}, {}),
|
||
|
('ColorProperty', ([1, 1, 1, 1],), {}, 'red', [1, 1, 1, 1]),
|
||
|
('BooleanProperty', (False,), {}, True, False),
|
||
|
('OptionProperty', ('a',), {'options': ['a', 'b']}, 'b', 'a'),
|
||
|
('StringProperty', ('',), {}, 'a', ''),
|
||
|
('ListProperty', ([],), {}, [1, 2], []),
|
||
|
('AliasProperty', (0,), {}, 1, 0),
|
||
|
('ReferenceListProperty', ((1, 2),), {}, (3, 4), (1, 2)),
|
||
|
])
|
||
|
@pytest.mark.parametrize('exclude_first', [True, False])
|
||
|
def test_property_set(
|
||
|
kivy_benchmark, name, args, kwargs, val, reset_val, exclude_first):
|
||
|
event_cls = get_event_class(name, args, kwargs)
|
||
|
|
||
|
# create one just so we don't incur loading cost
|
||
|
e = event_cls()
|
||
|
|
||
|
def set_property():
|
||
|
e.a = reset_val
|
||
|
e.a = val
|
||
|
|
||
|
if exclude_first:
|
||
|
set_property()
|
||
|
kivy_benchmark(set_property)
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize('n', [1, 10, 100, 1_000])
|
||
|
def test_widget_empty_draw(kivy_benchmark, n):
|
||
|
from kivy.graphics import RenderContext
|
||
|
from kivy.uix.widget import Widget
|
||
|
ctx = RenderContext()
|
||
|
root = Widget()
|
||
|
for x in range(n):
|
||
|
root.add_widget(Widget())
|
||
|
ctx.add(root.canvas)
|
||
|
|
||
|
kivy_benchmark(ctx.draw)
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize('n', [1, 10, 100, 1_000])
|
||
|
def test_widget_dispatch_touch(kivy_benchmark, n):
|
||
|
from kivy.tests.common import UnitTestTouch
|
||
|
from kivy.uix.widget import Widget
|
||
|
|
||
|
root = Widget()
|
||
|
for x in range(10):
|
||
|
parent = Widget()
|
||
|
for y in range(n):
|
||
|
parent.add_widget(Widget())
|
||
|
root.add_widget(parent)
|
||
|
|
||
|
touch = UnitTestTouch(10, 10)
|
||
|
|
||
|
def dispatch():
|
||
|
root.dispatch('on_touch_down', touch)
|
||
|
root.dispatch('on_touch_move', touch)
|
||
|
root.dispatch('on_touch_up', touch)
|
||
|
|
||
|
kivy_benchmark(dispatch)
|
||
|
|
||
|
|
||
|
@pytest.mark.parametrize('n', [1, 10, 100, 1_000])
|
||
|
@pytest.mark.parametrize('name', ['label', 'button'])
|
||
|
@pytest.mark.parametrize('tick', ['tick', 'no_tick'])
|
||
|
def test_random_label_create(kivy_benchmark, n, name, tick):
|
||
|
from kivy.clock import Clock
|
||
|
from kivy.uix.label import Label
|
||
|
from kivy.uix.button import Button
|
||
|
label = Label(text='*&^%')
|
||
|
button = Button(text='*&^%')
|
||
|
cls = Label if name == 'label' else Button
|
||
|
|
||
|
labels = []
|
||
|
k = len(ascii_letters)
|
||
|
for x in range(n):
|
||
|
label = [ascii_letters[randint(0, k - 1)] for _ in range(10)]
|
||
|
labels.append(''.join(label))
|
||
|
|
||
|
def make_labels():
|
||
|
o = []
|
||
|
for text in labels:
|
||
|
o.append(cls(text=text))
|
||
|
|
||
|
if tick == 'tick':
|
||
|
Clock.tick()
|
||
|
kivy_benchmark(make_labels)
|
||
|
|
||
|
|
||
|
def test_parse_kv(kivy_benchmark):
|
||
|
from kivy.lang import Builder
|
||
|
suffix = 0
|
||
|
|
||
|
def parse_kv():
|
||
|
nonlocal suffix
|
||
|
Builder.load_string(f"""
|
||
|
<MyWidget{suffix}>:
|
||
|
width: 55
|
||
|
height: 37
|
||
|
x: self.width + 5
|
||
|
y: self.height + 32
|
||
|
""")
|
||
|
suffix += 1
|
||
|
|
||
|
# create one just so we don't incur loading cost
|
||
|
parse_kv()
|
||
|
kivy_benchmark(parse_kv)
|