Skip to content

Commit

Permalink
Introduce package for Briefcase Automation
Browse files Browse the repository at this point in the history
  • Loading branch information
rmartin16 committed Nov 21, 2023
1 parent ccda123 commit 6eb07db
Show file tree
Hide file tree
Showing 12 changed files with 222 additions and 2 deletions.
10 changes: 8 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -145,10 +145,13 @@ jobs:
verify-templates:
name: Verify Templates
needs: unit-tests
uses: beeware/.github/.github/workflows/app-create-verify.yml@main
# uses: beeware/.github/.github/workflows/app-create-verify.yml@main
uses: rmartin16/.github-beeware/.github/workflows/app-create-verify.yml@build-verify-run
with:
runner-os: ${{ matrix.runner-os }}
framework: ${{ matrix.framework }}
workflow-repo: rmartin16/.github-beeware # TODO:PR: REMOVE ME
workflow-repo-ref: build-verify-run # TODO:PR: REMOVE ME
strategy:
fail-fast: false
matrix:
Expand All @@ -158,7 +161,8 @@ jobs:
verify-apps:
name: Build app
needs: unit-tests
uses: beeware/.github/.github/workflows/app-build-verify.yml@main
# uses: beeware/.github/.github/workflows/app-build-verify.yml@main
uses: rmartin16/.github-beeware/.github/workflows/app-build-verify.yml@build-verify-run
with:
# This *must* be the version of Python that is the system Python on the
# Ubuntu version used to run Linux tests. We use a fixed ubuntu-22.04
Expand All @@ -168,6 +172,8 @@ jobs:
python-version: "3.10"
runner-os: ${{ matrix.runner-os }}
framework: ${{ matrix.framework }}
workflow-repo: rmartin16/.github-beeware # TODO:PR: REMOVE ME
workflow-repo-ref: build-verify-run # TODO:PR: REMOVE ME
strategy:
fail-fast: false
matrix:
Expand Down
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,6 @@ recursive-include tests *.json
recursive-include tests *.out
recursive-include tests *.returncode
recursive-include tests *.config
recursive-include automation *.md
recursive-include automation *.py
recursive-include automation *.toml
12 changes: 12 additions & 0 deletions automation/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
## Briefcase Automation

This package provides Briefcase plugins to facilitate automation in CI.

This package is internal to Briefcase's own development and is not needed to create,
develop, or distribute apps created with Briefcase.

### Bootstraps

There are bootstrap plugins for each GUI toolkit, i.e. Toga, PySide6, PursuedPyBear,
and Pygame. The bootstrap allows for Briefcase to create a project using a toolkit and
when the project's app is run, the app automatically exits after a few seconds.
24 changes: 24 additions & 0 deletions automation/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[build-system]
requires = ["setuptools >= 67.0"]
build-backend = "setuptools.build_meta"

[project]
name = "briefcase-automation"
version = "0.1.0"
description = "A Briefcase plugin for CI automation."
license = {text = "BSD-3-Clause"}
classifiers = [
"Programming Language :: Python :: 3",
"License :: OSI Approved :: New BSD",
"Operating System :: OS Independent",
]
dependencies = [
"briefcase >= 0.3.17.dev100",
"toml",
]

[project.entry-points."briefcase.bootstraps"]
"Toga Automation" = "automation.bootstraps.toga:TogaAutomationBootstrap"
"PySide6 Automation" = "automation.bootstraps.pyside6:PySide6AutomationBootstrap"
"Pygame Automation" = "automation.bootstraps.pygame:PygameAutomationBootstrap"
"PursuedPyBear Automation" = "automation.bootstraps.pursuedpybear:PursuedPyBearAutomationBootstrap"
Empty file added automation/src/__init__.py
Empty file.
Empty file.
Empty file.
61 changes: 61 additions & 0 deletions automation/src/automation/bootstraps/pursuedpybear.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import toml

from briefcase.bootstraps import PursuedPyBearGuiBootstrap


class PursuedPyBearAutomationBootstrap(PursuedPyBearGuiBootstrap):
def app_source(self):
return """\
import importlib.metadata
import os
import sys
import ppb
class {{ cookiecutter.class_name }}(ppb.Scene):
def __init__(self, **props):
super().__init__(**props)
self.updates: int = 0
self.add(
ppb.Sprite(
image=ppb.Image("{{ cookiecutter.module_name }}/resources/{{ cookiecutter.app_name }}.png"),
)
)
def on_update(self, event, signal):
self.updates += 1
# quit after 2 seconds since on_update is run 60 times/second
if self.updates > 120:
print(">>> successfully started...exiting <<<")
print(">>>>>>>>>> EXIT 0 <<<<<<<<<<")
signal(ppb.events.Quit())
def main():
app_module = sys.modules["__main__"].__package__
metadata = importlib.metadata.metadata(app_module)
os.environ["SDL_VIDEO_X11_WMCLASS"] = metadata["Formal-Name"]
ppb.run(
starting_scene={{ cookiecutter.class_name }},
title=metadata["Formal-Name"],
)
"""

def pyproject_table_linux_flatpak(self):
table = toml.loads(super().pyproject_table_linux_flatpak())
table.setdefault("requires", []).append("pysdl2-dll==2.0.22")
return f"\n{toml.dumps(table)}"

def pyproject_table_windows(self):
table = toml.loads(super().pyproject_table_windows())
table.setdefault("requires", []).append("pysdl2-dll==2.0.22")
return f"\n{toml.dumps(table)}"

def pyproject_table_macOS(self):
table = toml.loads(super().pyproject_table_macOS())
table.setdefault("requires", []).append("pysdl2-dll==2.0.22")
return f"\n{toml.dumps(table)}"
42 changes: 42 additions & 0 deletions automation/src/automation/bootstraps/pygame.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from briefcase.bootstraps import PygameGuiBootstrap


class PygameAutomationBootstrap(PygameGuiBootstrap):
def app_source(self):
return """\
import importlib.metadata
import os
import sys
import pygame
SCREEN_WIDTH, SCREEN_HEIGHT = 800, 600
WHITE = (255, 255, 255)
def main():
app_module = sys.modules["__main__"].__package__
metadata = importlib.metadata.metadata(app_module)
os.environ["SDL_VIDEO_X11_WMCLASS"] = metadata["Formal-Name"]
pygame.init()
pygame.display.set_caption(metadata["Formal-Name"])
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
pygame.time.set_timer(pygame.QUIT, 2000)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
print(">>> successfully started...exiting <<<")
print(">>>>>>>>>> EXIT 0 <<<<<<<<<<")
running = False
break
screen.fill(WHITE)
pygame.display.flip()
pygame.quit()
"""
40 changes: 40 additions & 0 deletions automation/src/automation/bootstraps/pyside6.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from briefcase.bootstraps import PySide6GuiBootstrap


class PySide6AutomationBootstrap(PySide6GuiBootstrap):
def app_source(self):
return """\
import importlib.metadata
import sys
from PySide6 import QtWidgets
from PySide6.QtCore import QTimer
class {{ cookiecutter.class_name }}(QtWidgets.QMainWindow):
def __init__(self):
super().__init__()
self.init_ui()
def init_ui(self):
self.setWindowTitle("{{ cookiecutter.app_name }}")
self.show()
QTimer.singleShot(2000, self.exit_app)
def exit_app(self):
print(">>> successfully started...exiting <<<")
print(">>>>>>>>>> EXIT 0 <<<<<<<<<<")
QtWidgets.QApplication.quit()
def main():
app_module = sys.modules["__main__"].__package__
metadata = importlib.metadata.metadata(app_module)
QtWidgets.QApplication.setApplicationName(metadata["Formal-Name"])
app = QtWidgets.QApplication(sys.argv)
main_window = {{ cookiecutter.class_name }}()
sys.exit(app.exec())
"""
31 changes: 31 additions & 0 deletions automation/src/automation/bootstraps/toga.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from briefcase.bootstraps import TogaGuiBootstrap


class TogaAutomationBootstrap(TogaGuiBootstrap):
def app_source(self):
return '''\
import asyncio
import toga
class {{ cookiecutter.class_name }}(toga.App):
def startup(self):
"""Construct and show the Toga application."""
self.main_window = toga.MainWindow(title=self.formal_name)
self.main_window.show()
self.add_background_task(self.exit_soon)
async def exit_soon(self, app: toga.App, **kwargs):
"""Background task that closes the app after a few seconds."""
await asyncio.sleep(2)
print(">>> successfully started...exiting <<<")
print(">>>>>>>>>> EXIT 0 <<<<<<<<<<")
self.exit()
def main():
return {{ cookiecutter.class_name }}()
'''
1 change: 1 addition & 0 deletions changes/1549.misc.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
The Briefcase Automation package was created to facilitate automated testing in CI; for example, starting apps built in CI that can automatically exit.

0 comments on commit 6eb07db

Please sign in to comment.