Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Test app #1687

Merged
merged 34 commits into from
Dec 7, 2022
Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
d8e33fd
Test app WIP
mhsmith Nov 15, 2022
5ce684a
Fix threading and discovery
mhsmith Nov 21, 2022
90f1873
Split common and backend test code, add more tests
mhsmith Nov 23, 2022
71d7487
Cleanups
mhsmith Nov 28, 2022
0481126
Split test and production code
mhsmith Nov 28, 2022
06dc8ac
Add Android probes
mhsmith Nov 28, 2022
296b424
Add Slider tests
mhsmith Nov 29, 2022
fcd6b9a
Remove `tests` from isort known_first_party list
mhsmith Nov 29, 2022
0d65ecf
Add test_probes to MANIFEST.in
mhsmith Nov 29, 2022
da146a0
Work around https://github.com/pypa/twine/issues/940, and pin all oth…
mhsmith Nov 29, 2022
ad710a9
Remove stray comment
mhsmith Nov 29, 2022
d50e9fd
Remove `dummy` from test app requirements
mhsmith Nov 30, 2022
0593b52
Replace global `app` variable with `toga.App.app`
mhsmith Nov 30, 2022
2c97ac3
Split "utils" and "common" modules into more specific names
mhsmith Nov 30, 2022
87287de
Rename test_probes to tests_backend, and probe_{name}.py to {name}.py
mhsmith Dec 3, 2022
8beba38
Generalize probe constructors / Create a new container for each test
mhsmith Dec 3, 2022
c3cf437
Reduce colors list
mhsmith Dec 3, 2022
94594ab
Update backends' MANIFEST.in / Update twine
mhsmith Dec 3, 2022
8a62cec
Rename toga-test to testbed.
freakboy3742 Dec 5, 2022
9e6c692
Backfill all required icons.
freakboy3742 Dec 5, 2022
5d2dd6c
Add a reminder to use --test.
freakboy3742 Dec 5, 2022
dd8739c
Add a CI configuration to run testbed.
freakboy3742 Dec 5, 2022
1ddcb53
Add workaround for Rubicon thread starvation problem.
freakboy3742 Dec 5, 2022
20130d1
Only use the workaround on iOS.
freakboy3742 Dec 5, 2022
5d9982a
Clarify the role played by main_loop() on iOS.
freakboy3742 Dec 6, 2022
018fb41
Ensure paths are correct before running the test suite.
freakboy3742 Dec 6, 2022
3ae99ae
Merge pull request #1 from freakboy3742/testbed
mhsmith Dec 6, 2022
a9580ce
Remove Codecov
mhsmith Dec 6, 2022
426ae43
Remove questionable comment
mhsmith Dec 6, 2022
89f99b8
Add **kwargs to event handlers
mhsmith Dec 6, 2022
30f4c82
Make test skip conditions more specific
mhsmith Dec 6, 2022
347b112
Move native container assertions from probe constructor into a separa…
mhsmith Dec 6, 2022
9d6c88c
Add xvfb-run to GTK testbed CI
mhsmith Dec 7, 2022
30295c1
Fix typo
mhsmith Dec 7, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions android/MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ include LICENSE
include README.rst
include tox.ini
recursive-include tests *.py
recursive-include test_probes *.py
Empty file added android/test_probes/__init__.py
Empty file.
15 changes: 15 additions & 0 deletions android/test_probes/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from java import jint

from android.graphics import Color
from toga.colors import rgba


def toga_color(color_int):
# Select the `int` overloads rather than the `long` ones.
color_int = jint(color_int)
return rgba(
Color.red(color_int),
Color.green(color_int),
Color.blue(color_int),
Color.alpha(color_int) / 255,
)
Empty file.
40 changes: 40 additions & 0 deletions android/test_probes/widgets/probe_base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from pytest import skip


class SimpleProbe:
def __init__(self, main_box, widget):
native_box = main_box._impl.native
assert native_box.getChildCount() == 1
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not clear to me why this is a feature of the probe, rather than an assertion related to the layout. This structure seems to be strongly bound to a specific layout; when we start adding more complex tests of layout, we're going to need entirely separate probe classes to support each possible layout scheme.

Would it make more sense to make Probe be purely the "probing" bits; write a probe for Box to abstract the "get my list of children"-type behavior, and add an assertion that uses the box probe and the widget probe in combination to verify impl relationships?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point: I've reduced the Probe constructors to just be "find this widget inside this container", and any more specific layout assertions can wait until we write the layout tests.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And I've separated out the function that instantiates the probe so it can be called from more complex layouts.

self.native = native_box.getChildAt(0)
assert isinstance(self.native, self.native_class)

# Although this isn't part of the public API, we often point users at it to do
# things that Toga itself doesn't support.
assert widget._impl.native is self.native

@property
def enabled(self):
return self.native.isEnabled()

@property
def background_color(self):
skip("not implemented: background_color")

@property
def color(self):
skip("not implemented: color")

@property
def hidden(self):
skip("not implemented: hidden")

@property
def width(self):
return self.native.getWidth()

@property
def height(self):
return self.native.getHeight()

def press(self):
self.native.performClick()
8 changes: 8 additions & 0 deletions android/test_probes/widgets/probe_button.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from java import jclass
mhsmith marked this conversation as resolved.
Show resolved Hide resolved

from .probe_label import LabelProbe


# On Android, a Button is just a TextView with a state-dependent background image.
class ButtonProbe(LabelProbe):
native_class = jclass("android.widget.Button")
16 changes: 16 additions & 0 deletions android/test_probes/widgets/probe_label.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from java import jclass

from ..utils import toga_color
from .probe_base import SimpleProbe


class LabelProbe(SimpleProbe):
native_class = jclass("android.widget.TextView")

@property
def color(self):
return toga_color(self.native.getCurrentTextColor())

@property
def text(self):
return str(self.native.getText())
24 changes: 24 additions & 0 deletions android/test_probes/widgets/probe_slider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from java import jclass

from android.os import Build

from .probe_base import SimpleProbe


class SliderProbe(SimpleProbe):
native_class = jclass("android.widget.SeekBar")

@property
def position(self):
return (self.native.getProgress() - self._min) / (self._max - self._min)

def change(self, position):
self.native.setProgress(self._min + round(position * (self._max - self._min)))

@property
def _min(self):
return 0 if (Build.VERSION.SDK_INT < 26) else self.native.getMin()

@property
def _max(self):
return self.native.getMax()
1 change: 1 addition & 0 deletions changes/1687.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Added a backend test app.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ known_first_party = [
"toga_dummy",
"toga_gtk",
"toga_iOS",
"toga_test",
"toga_web",
"toga_winforms",
]
Expand Down
69 changes: 69 additions & 0 deletions test/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

# OSX useful to ignore
*.DS_Store
.AppleDouble
.LSOverride

# Thumbnails
._*

# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent

# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk

# C extensions
*.so

# Distribution / packaging
.Python
env/
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
*.egg-info/
.installed.cfg
*.egg

# IntelliJ Idea family of suites
.idea
*.iml
## File-based project format:
*.ipr
*.iws
## mpeltonen/sbt-idea plugin
.idea_modules/

# Briefcase build directories
iOS/
macOS/
windows/
android/
linux/
django/

# Briefcase log files
logs/
91 changes: 91 additions & 0 deletions test/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# This project was generated using template: https://github.com/beeware/briefcase-template and branch: v0.3.12
[tool.briefcase]
project_name = "Toga test"
bundle = "org.beeware"
version = "0.0.1"
url = "https://beeware.org"
license = "BSD license"
author = 'Tiberius Yak'
author_email = "[email protected]"

[tool.briefcase.app.toga-test]
formal_name = "Toga test"
description = "Toga test"
icon = "src/toga_test/resources/toga_test"
sources = [
'src/toga_test',
]
test_sources = [
'tests',
]
requires = [
'../core',
'../dummy',
freakboy3742 marked this conversation as resolved.
Show resolved Hide resolved
]
test_requires = [
'pytest==7.2.0',
'pytest-asyncio==0.20.2',
]


[tool.briefcase.app.toga-test.macOS]
requires = [
'../cocoa',
'std-nslog~=1.0.0'
]

[tool.briefcase.app.toga-test.linux]
requires = [
'../gtk',
]

[tool.briefcase.app.toga-test.linux.appimage]
system_requires = [
'gir1.2-webkit-3.0',
'libcairo2-dev',
'libgirepository1.0-dev',
'libgtk-3-dev',
'libpango1.0-dev',
'librsvg2-dev',
'libwebkitgtk-3.0-0',
]
linuxdeploy_plugins = [
'DEPLOY_GTK_VERSION=3 gtk',
]

[tool.briefcase.app.toga-test.linux.flatpak]
flatpak_runtime = 'org.gnome.Platform'
flatpak_runtime_version = '42'
flatpak_sdk = 'org.gnome.Sdk'

[tool.briefcase.app.toga-test.windows]
test_sources = [
'../winforms/test_probes',
]
requires = [
'../winforms',
]

# Mobile deployments
[tool.briefcase.app.toga-test.iOS]
requires = [
'../iOS',
'std-nslog~=1.0.0'
]

[tool.briefcase.app.toga-test.android]
test_sources = [
'../android/test_probes',
]
requires = [
'../android'
]

# TODO: replace with extractPackages
build_gradle_extra_content = "android.defaultConfig.python.pyc.src false"

[tool.briefcase.app.toga-test.web]
requires = [
'../web'
]
style_framework = "Bootstrap v4.6"
Empty file added test/src/toga_test/__init__.py
Empty file.
4 changes: 4 additions & 0 deletions test/src/toga_test/__main__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from toga_test.app import main

if __name__ == "__main__":
main().main_loop()
18 changes: 18 additions & 0 deletions test/src/toga_test/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import toga


class TogaTest(toga.App):
def startup(self):
# For the "app" fixture.
global app
app = self
freakboy3742 marked this conversation as resolved.
Show resolved Hide resolved

self.main_box = toga.Box()

self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.content = self.main_box
self.main_window.show()


def main():
return TogaTest(app_name="toga_test")
mhsmith marked this conversation as resolved.
Show resolved Hide resolved
Empty file.
Binary file added test/src/toga_test/resources/toga_test.icns
Binary file not shown.
Binary file added test/src/toga_test/resources/toga_test.ico
Binary file not shown.
Binary file added test/src/toga_test/resources/toga_test.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Empty file added test/tests/__init__.py
Empty file.
57 changes: 57 additions & 0 deletions test/tests/conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import asyncio
import inspect
from dataclasses import dataclass

from pytest import fixture

import toga_test.app


@fixture(scope="session")
def app():
freakboy3742 marked this conversation as resolved.
Show resolved Hide resolved
return toga_test.app.app


@fixture(scope="session")
def main_box(app):
return app.main_box


# Controls the event loop used by pytest-asyncio.
@fixture(scope="session")
def event_loop(app):
return ProxyEventLoop(app._impl.loop)
freakboy3742 marked this conversation as resolved.
Show resolved Hide resolved


# Proxy which forwards all tasks to another event loop in a thread-safe manner. It
# implements only the methods used by pytest-asyncio.
@dataclass
class ProxyEventLoop(asyncio.AbstractEventLoop):
loop: object

# Used by ensure_future.
def create_task(self, coro):
return ProxyTask(coro)

def run_until_complete(self, future):
if inspect.iscoroutine(future):
coro = future
elif isinstance(future, ProxyTask):
coro = future.coro
else:
raise TypeError(f"Future type {type(future)} is not currently supported")
return asyncio.run_coroutine_threadsafe(coro, self.loop).result()

def close(self):
pass


@dataclass
class ProxyTask:
coro: object

# Used by ensure_future.
_source_traceback = None

def done(self):
return False
Loading