forked from LibrePCB/LibrePCB
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add first functional tests with "funq"
- Loading branch information
Showing
14 changed files
with
602 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
pytest.ini | ||
gitems.json | ||
widgets_list.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
# Functional Tests | ||
|
||
This directory contains functional tests (simulating user input) using | ||
[funq](https://github.com/parkouss/funq) and [pytest](https://docs.pytest.org). | ||
|
||
## Create Virtualenv | ||
|
||
mkvirtualenv -p `which python3` librepcb-funq | ||
|
||
## Install Requirements | ||
|
||
pip install -r requirements.txt | ||
|
||
## Run Tests | ||
|
||
pytest -v --librepcb-executable=/path/to/librepcb | ||
|
||
## Links | ||
|
||
- [Documentation of `funq`](http://funq.readthedocs.io/en/latest/) | ||
- [Documentation of `pytest`](https://docs.pytest.org/en/latest/contents.html) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,145 @@ | ||
################################################################################ | ||
# Control Panel | ||
################################################################################ | ||
|
||
# Main window | ||
controlPanel = librepcb__application__ControlPanel | ||
controlPanelWidget = {controlPanel}::centralWidget | ||
|
||
# Menu | ||
controlPanelMenuBar = {controlPanel}::menuBar | ||
controlPanelMenuExtras = {controlPanelMenuBar}::menuExtras | ||
|
||
# Labels at top | ||
controlPanelWarnForNoLibrariesLabel = {controlPanelWidget}::lblWarnForNoLibraries | ||
|
||
# Buttons in the top right frame | ||
controlPanelButtonsFrame = {controlPanelWidget}::splitter_h::layoutWidget1::frame | ||
controlPanelNewProjectButton = {controlPanelButtonsFrame}::newProjectButton | ||
controlPanelOpenProjectButton = {controlPanelButtonsFrame}::openProjectButton | ||
controlPanelOpenLibraryManagerButton = {controlPanelButtonsFrame}::openLibraryManagerButton | ||
|
||
# Open project dialog | ||
controlPanelOpenProjectDialog = {controlPanel}::{QT_FILE_DIALOG} | ||
controlPanelOpenProjectDialogFileNameEdit = {controlPanel}::{QT_FILE_DIALOG_LINE_EDIT} | ||
controlPanelOpenProjectDialogOkButton = {controlPanel}::{QT_FILE_DIALOG_BTN_OK} | ||
|
||
# New project wizard | ||
controlPanelNewProjectWizard = {controlPanel}::librepcb__project__editor__NewProjectWizard | ||
controlPanelNewProjectWizardWidget = {controlPanelNewProjectWizard}::QWidget | ||
controlPanelNewProjectWizardNextButton = {controlPanelNewProjectWizardWidget}::__qt__passive_wizardbutton1 | ||
controlPanelNewProjectWizardFinishButton = {controlPanelNewProjectWizardWidget}::qt_wizard_finish | ||
controlPanelNewProjectWizardFrame = {controlPanelNewProjectWizardWidget}::QFrame | ||
controlPanelNewProjectWizardMetadataPage = {controlPanelNewProjectWizardFrame}::librepcb__project__editor__NewProjectWizardPage_Metadata | ||
controlPanelNewProjectWizardMetadataNameEdit = {controlPanelNewProjectWizardMetadataPage}::edtName | ||
controlPanelNewProjectWizardMetadataPathLabel = {controlPanelNewProjectWizardMetadataPage}::lblFullFilePath | ||
|
||
|
||
################################################################################ | ||
# Library Manager | ||
################################################################################ | ||
|
||
# Main window | ||
libraryManager = {controlPanel}::librepcb__library__manager__LibraryManager | ||
libraryManagerWidget = {libraryManager}::centralwidget | ||
|
||
# Installed libraries list | ||
libraryManagerInstalledLibrariesList = {libraryManagerWidget}::lstLibraries | ||
libraryManagerInstalledLibrariesListViewport = {libraryManagerInstalledLibrariesList}::qt_scrollarea_viewport | ||
|
||
# Add library widget | ||
libraryManagerAddLibraryWidget = {libraryManagerWidget}::librepcb__library__manager____AddLibraryWidget | ||
libraryManagerAddLibraryTabWidget = {libraryManagerAddLibraryWidget}::tabWidget | ||
libraryManagerAddLibraryTabBar = {libraryManagerAddLibraryTabWidget}::qt_tabwidget_tabbar | ||
libraryManagerAddLibraryStackedWidget = {libraryManagerAddLibraryTabWidget}::qt_tabwidget_stackedwidget | ||
|
||
# Remote libraries tab | ||
libraryManagerDownloadFromRepoTab = {libraryManagerAddLibraryStackedWidget}::tabDownloadFromRepo | ||
libraryManagerDownloadFromRepoLibraryList = {libraryManagerDownloadFromRepoTab}::lstRepoLibs | ||
libraryManagerDownloadFromRepoLibraryListViewport = {libraryManagerDownloadFromRepoLibraryList}::qt_scrollarea_viewport | ||
libraryManagerDownloadFromRepoLibraryListItem = {libraryManagerDownloadFromRepoLibraryListViewport}::librepcb__library__manager__RepositoryLibraryListWidgetItem | ||
libraryManagerDownloadFromRepoDownloadButton = {libraryManagerDownloadFromRepoTab}::btnRepoLibsDownload | ||
|
||
# Create local library tab | ||
libraryManagerCreateLocalLibraryTab = {libraryManagerAddLibraryStackedWidget}::tabCreateLocal | ||
libraryManagerCreateLocalLibraryNameEdit = {libraryManagerCreateLocalLibraryTab}::edtLocalName | ||
libraryManagerCreateLocalLibraryDescriptionEdit = {libraryManagerCreateLocalLibraryTab}::edtLocalDescription | ||
libraryManagerCreateLocalLibraryAuthorEdit = {libraryManagerCreateLocalLibraryTab}::edtLocalAuthor | ||
libraryManagerCreateLocalLibraryUrlEdit = {libraryManagerCreateLocalLibraryTab}::edtLocalUrl | ||
libraryManagerCreateLocalLibraryVersionEdit = {libraryManagerCreateLocalLibraryTab}::edtLocalVersion | ||
libraryManagerCreateLocalLibraryCc0LicenseCheckbox = {libraryManagerCreateLocalLibraryTab}::cbxLocalCc0License | ||
libraryManagerCreateLocalLibraryDirectoryEdit = {libraryManagerCreateLocalLibraryTab}::edtLocalDirectory | ||
libraryManagerCreateLocalLibraryCreateButton = {libraryManagerCreateLocalLibraryTab}::btnLocalCreate | ||
|
||
# Library info widget | ||
libraryManagerLibraryInfoWidget = {libraryManagerWidget}::librepcb__library__manager__LibraryInfoWidget | ||
libraryManagerLibraryInfoWidgetOpenEditorButton = {libraryManagerLibraryInfoWidget}::btnOpenLibraryEditor | ||
|
||
|
||
################################################################################ | ||
# Library Editor | ||
################################################################################ | ||
|
||
# Main window | ||
libraryEditor = librepcb__library__editor__LibraryEditor | ||
libraryEditorWidget = {libraryEditor}::centralWidget | ||
|
||
# Tab widget | ||
libraryEditorTabWidget = {libraryEditorWidget}::tabWidget | ||
libraryEditorStackedWidget = {libraryEditorTabWidget}::qt_tabwidget_stackedwidget | ||
|
||
# Library overview widget | ||
libraryEditorOverviewWidget = {libraryEditorStackedWidget}::librepcb__library__editor__LibraryOverviewWidget | ||
libraryEditorOverviewMetadataWidget = {libraryEditorOverviewWidget}::splitter::layoutWidget | ||
libraryEditorOverviewNameEdit = {libraryEditorOverviewMetadataWidget}::edtName | ||
libraryEditorOverviewDescriptionEdit = {libraryEditorOverviewMetadataWidget}::edtDescription | ||
libraryEditorOverviewKeywordsEdit = {libraryEditorOverviewMetadataWidget}::edtKeywords | ||
libraryEditorOverviewAuthorEdit = {libraryEditorOverviewMetadataWidget}::edtAuthor | ||
libraryEditorOverviewVersionEdit = {libraryEditorOverviewMetadataWidget}::edtVersion | ||
libraryEditorOverviewDeprecatedCheckbox = {libraryEditorOverviewMetadataWidget}::cbxDeprecated | ||
libraryEditorOverviewUrlEdit = {libraryEditorOverviewMetadataWidget}::edtUrl | ||
|
||
# New element wizard | ||
libraryEditorNewElementWizard = librepcb__library__editor__NewElementWizard | ||
libraryEditorNewElementWizardWidget = {libraryEditorNewElementWizard}::centralWidget | ||
libraryEditorNewElementWizardNextButton = {libraryEditorNewElementWizardWidget}::__qt__passive_wizardbutton1 | ||
libraryEditorNewElementWizardFinishButton = {libraryEditorNewElementWizardWidget}::qt_wizard_finish | ||
libraryEditorNewElementWizardFrame = {libraryEditorNewElementWizard}::QFrame | ||
libraryEditorNewElementWizardChooseTypePage = {libraryEditorNewElementWizardFrame}::librepcb__library__editor__NewElementWizardPage_ChooseType | ||
libraryEditorNewElementWizardChooseTypeCmpCatButton = {libraryEditorNewElementWizardChooseTypePage}::btnComponentCategory | ||
libraryEditorNewElementWizardChooseTypePkgCatButton = {libraryEditorNewElementWizardChooseTypePage}::btnPackageCategory | ||
libraryEditorNewElementWizardChooseTypeSymbolButton = {libraryEditorNewElementWizardChooseTypePage}::btnSymbol | ||
libraryEditorNewElementWizardChooseTypePackageButton = {libraryEditorNewElementWizardChooseTypePage}::btnPackage | ||
libraryEditorNewElementWizardChooseTypeComponentButton = {libraryEditorNewElementWizardChooseTypePage}::btnComponent | ||
libraryEditorNewElementWizardChooseTypeDeviceButton = {libraryEditorNewElementWizardChooseTypePage}::btnDevice | ||
libraryEditorNewElementWizardMetadataPage = {libraryEditorNewElementWizardFrame}::librepcb__library__editor__NewElementWizardPage_EnterMetadata | ||
libraryEditorNewElementWizardMetadataNameEdit = {libraryEditorNewElementWizardMetadataPage}::edtName | ||
libraryEditorNewElementWizardMetadataDescriptionEdit = {libraryEditorNewElementWizardMetadataPage}::edtDescription | ||
libraryEditorNewElementWizardMetadataKeywordsEdit = {libraryEditorNewElementWizardMetadataPage}::edtKeywords | ||
libraryEditorNewElementWizardMetadataAuthorEdit = {libraryEditorNewElementWizardMetadataPage}::edtAuthor | ||
libraryEditorNewElementWizardMetadataVersionEdit = {libraryEditorNewElementWizardMetadataPage}::edtVersion | ||
libraryEditorNewElementWizardMetadataCategoryEdit = {libraryEditorNewElementWizardMetadataPage}::edtCategory | ||
libraryEditorNewElementWizardMetadataCategoryLabel = {libraryEditorNewElementWizardMetadataPage}::lblCategoryTree | ||
libraryEditorNewElementWizardMetadataCategoryButton = {libraryEditorNewElementWizardMetadataPage}::btnChooseCategory | ||
|
||
|
||
################################################################################ | ||
# Schematic Editor | ||
################################################################################ | ||
|
||
# Main window | ||
schematicEditor = librepcb__project__editor__SchematicEditor | ||
schematicEditorWidget = {schematicEditor}::centralWidget | ||
|
||
# Graphics view | ||
schematicEditorGraphicsView = {schematicEditor}::librepcb:_:GraphicsView | ||
schematicEditorGraphicsViewWidget = {schematicEditorGraphicsView}::QWidget | ||
|
||
|
||
################################################################################ | ||
# Board Editor | ||
################################################################################ | ||
|
||
# Main window | ||
boardEditor = librepcb__project__editor__BoardEditor | ||
boardEditorWidget = {boardEditor}::centralWidget |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
|
||
import os | ||
import platform | ||
import shutil | ||
import pytest | ||
import funq | ||
from funq.client import ApplicationContext, ApplicationConfig | ||
|
||
|
||
FUNQ_DIR = os.path.dirname(__file__) | ||
TESTS_DIR = os.path.dirname(FUNQ_DIR) | ||
REPO_DIR = os.path.dirname(TESTS_DIR) | ||
DATA_DIR = os.path.join(TESTS_DIR, 'data') | ||
FUNQ_DATA_DIR = os.path.join(DATA_DIR, 'funq') | ||
FIXTURES_DATA_DIR = os.path.join(FUNQ_DATA_DIR, 'fixtures') | ||
|
||
|
||
def pytest_addoption(parser): | ||
parser.addoption("--librepcb-executable", | ||
action="store", | ||
help="Path to librepcb executable to test") | ||
|
||
|
||
class GlobalOptions: | ||
def __init__(self): | ||
self.funq_conf = 'funq.conf' | ||
self.funq_attach_exe = funq.tools.which('funq') | ||
self.funq_gkit = 'default' | ||
self.funq_gkit_file = os.path.join(os.path.dirname(os.path.realpath(funq.client.__file__)), 'aliases-gkits.conf') | ||
|
||
|
||
class Application(object): | ||
def __init__(self, executable, env=None, args=()): | ||
super(Application, self).__init__() | ||
cfg = ApplicationConfig(executable=executable, args=args, cwd=os.getcwd(), env=env, | ||
aliases=os.path.join(FUNQ_DIR, 'aliases'), global_options=GlobalOptions()) | ||
self._context = ApplicationContext(cfg) | ||
|
||
def __enter__(self): | ||
return self._context.funq | ||
|
||
def __exit__(self, exc_type, exc_val, exc_tb): | ||
del self._context | ||
|
||
|
||
class LibrePcbFixture(object): | ||
def __init__(self, config, tmpdir): | ||
super(LibrePcbFixture, self).__init__() | ||
self.executable = os.path.abspath(config.getoption('--librepcb-executable')) | ||
if not os.path.exists(self.executable): | ||
raise Exception("Executable '{}' not found. Please pass it with " | ||
"'--librepcb-executable'.".format(self.executable)) | ||
self.tmpdir = tmpdir | ||
# Copy test data to temporary directory to avoid modifications in original data | ||
self.data_dir = os.path.join(self.tmpdir, 'data') | ||
shutil.copytree(FUNQ_DATA_DIR, self.data_dir) | ||
# Init members to default values | ||
self.workspace_path = os.path.join(self.data_dir, 'fixtures', 'Empty Workspace') | ||
self.project_path = None | ||
|
||
def abspath(self, relpath): | ||
return os.path.join(self.tmpdir, relpath) | ||
|
||
def set_workspace(self, path): | ||
if not os.path.isabs(path): | ||
path = self.abspath(path) | ||
self.workspace_path = path | ||
|
||
def set_project(self, path): | ||
if not os.path.isabs(path): | ||
path = self.abspath(path) | ||
self.project_path = path | ||
|
||
def add_local_library_to_workspace(self, path='data/fixtures/Empty Library.lplib'): | ||
if not os.path.isabs(path): | ||
path = self.abspath(path) | ||
dest = os.path.join(self.workspace_path, 'v0.1', 'libraries', 'local') | ||
dest = os.path.join(dest, os.path.basename(path)) | ||
shutil.copytree(path, dest) | ||
|
||
def open(self): | ||
self._create_application_config_file() | ||
return Application(self.executable, env=self._env(), args=self._args()) | ||
|
||
def _create_application_config_file(self): | ||
org_dir = 'LibrePCB.org' if platform.system() == 'Darwin' else 'LibrePCB' | ||
config_dir = os.path.join(self.tmpdir, 'config', org_dir) | ||
config_ini = os.path.join(config_dir, 'LibrePCB.ini') | ||
if not os.path.exists(config_dir): | ||
os.makedirs(config_dir) | ||
with open(config_ini, 'w') as f: | ||
if self.workspace_path: | ||
f.write("[workspaces]\n") | ||
f.write("most_recently_used=\"{}\"\n".format(self.workspace_path.replace('\\', '/'))) | ||
|
||
def _args(self): | ||
args = [] | ||
if self.project_path: | ||
args.append(self.project_path) | ||
return args | ||
|
||
def _env(self): | ||
env = os.environ | ||
# Make GUI independent from the system's language | ||
env['LC_ALL'] = 'C' | ||
# Override configuration location to make tests independent of existing configs | ||
env['LIBREPCB_CONFIG_DIR'] = os.path.join(self.tmpdir, 'config') | ||
# Use a neutral username | ||
env['USERNAME'] = 'testuser' | ||
# Force LibrePCB to use Qt-style file dialogs because native dialogs don't work | ||
env['LIBREPCB_DISABLE_NATIVE_DIALOGS'] = '1' | ||
return env | ||
|
||
|
||
@pytest.fixture(scope="session") | ||
def librepcb_server(): | ||
""" | ||
Fixture which provides a HTTP server at localhost:8080 | ||
All tests should use this server instead of the official LibrePCB API server | ||
or GitHub for downloading libraries. | ||
""" | ||
import time | ||
import threading | ||
import socket | ||
import socketserver | ||
import http.server | ||
|
||
class Handler(http.server.SimpleHTTPRequestHandler, object): | ||
def translate_path(self, path): | ||
path = super(Handler, self).translate_path(path) | ||
relpath = os.path.relpath(path, os.curdir) | ||
return os.path.join(FIXTURES_DATA_DIR, 'server', relpath) | ||
|
||
# Set SO_REUSEADDR option to avoid "port already in use" errors | ||
httpd = socketserver.TCPServer(("", 50080), Handler, bind_and_activate=False) | ||
httpd.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) | ||
httpd.server_bind() | ||
httpd.server_activate() | ||
thread = threading.Thread(target=httpd.serve_forever) | ||
thread.daemon = True | ||
thread.start() | ||
time.sleep(0.2) # wait a bit to make sure the server is ready | ||
|
||
|
||
@pytest.fixture | ||
def create_librepcb(request, tmpdir, librepcb_server): | ||
""" | ||
Fixture allowing to create multiple application instances | ||
""" | ||
def _create(): | ||
return LibrePcbFixture(request.config, str(tmpdir)) | ||
return _create | ||
|
||
|
||
@pytest.fixture | ||
def librepcb(create_librepcb): | ||
""" | ||
Fixture allowing to create one application instance | ||
""" | ||
yield create_librepcb() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
#!/usr/bin/env python | ||
# -*- coding: utf-8 -*- | ||
|
||
""" | ||
Test creating projects | ||
""" | ||
|
||
import os | ||
|
||
|
||
def test_new_project_wizard(librepcb): | ||
""" | ||
Create project using the wizard from the control panel | ||
""" | ||
with librepcb.open() as app: | ||
# Open new project wizard | ||
app.widget('controlPanelNewProjectButton').click() | ||
assert app.widget('controlPanelNewProjectWizard').properties()['visible'] is True | ||
# Enter metadata | ||
name = 'New Project' | ||
app.widget('controlPanelNewProjectWizardMetadataNameEdit').set_property('text', name) | ||
path = app.widget('controlPanelNewProjectWizardMetadataPathLabel').properties()['text'] | ||
app.widget('controlPanelNewProjectWizardNextButton').click() | ||
# Setup schematic/board | ||
app.widget('controlPanelNewProjectWizardFinishButton').click() | ||
# Verify if editors are opened and project file exists | ||
assert app.widget('schematicEditor').properties()['visible'] is True | ||
assert app.widget('boardEditor').properties()['visible'] is True | ||
assert os.path.exists(path) | ||
|
||
# Open project again to see if it was saved properly | ||
librepcb.set_project(path) | ||
with librepcb.open() as app: | ||
assert app.widget('schematicEditor').properties()['visible'] is True | ||
assert app.widget('boardEditor').properties()['visible'] is True |
Oops, something went wrong.