diff --git a/.gitignore b/.gitignore
index 46e5469eeaa0cdc2749a696de34e248d8efb6aa5..179e5e16ed9770732dc73934dddc8571a25f7bd9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,9 @@
+# ignore html directory that contains output of pdoc
+html
+
+# gitlab pages directory
+public
+
# virtual environment folder
venv
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cd37ccfd0697a141ad8a9674e7cd361f82a763ce..a2ebe19ff16ae52b8d1ed92ae766b15b9695c13e 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -5,41 +5,36 @@ stages:
job_lint_flake8:
stage: lint
- image: morkolai/paa-bittet-ci
+ image: morkolai/soitool-ci
script:
- flake8 --version
- - flake8 soitool
+ - flake8 --config scripts/.flake8 soitool test
job_lint_pylint:
stage: lint
- image: morkolai/paa-bittet-ci
+ image: morkolai/soitool-ci
script:
- pylint --version
- - pip install pyside2
- - pylint --rcfile=scripts/.pylintrc soitool
+ - pylint --rcfile=scripts/.pylintrc soitool test
job_lint_bandit:
stage: lint
- image: morkolai/paa-bittet-ci
+ image: morkolai/soitool-ci
script:
- bandit --version
- - bandit -r soitool
+ - bandit -r soitool test
job_lint_pydocstyle:
stage: lint
- image: morkolai/paa-bittet-ci
+ image: morkolai/soitool-ci
script:
- - pip install pydocstyle
- pydocstyle --version
- - pydocstyle --convention=numpy soitool
+ - pydocstyle --match '.*.py' --convention=numpy soitool test
job_test_gui_ubuntu_vnc:
stage: test
- image: morkolai/paa-bittet-ci
- before_script:
- - apt-get install -y libgl1-mesa-glx
+ image: morkolai/soitool-ci
script:
- - pip install pyside2
# -platform because running with a screen is not supported
# https://stackoverflow.com/questions/17106315/failed-to-load-platform-plugin-xcb-while-launching-qt5-app-on-linux-without
- QT_QPA_PLATFORM=vnc python3 -m unittest test.test_main
@@ -52,21 +47,20 @@ job_test_gui_windows:
- python --version
- python -m unittest test.test_main
-#job_test_gui_ubuntu:
-# stage: test
-# tags:
-# - ci-ubuntu
-# script:
-# - python3 --version
-# - DISPLAY=':10.0' python3 -m unittest test.test_main
+job_test_gui_ubuntu:
+ stage: test
+ tags:
+ - ci-ubuntu
+ script:
+ - python3 --version
+ - DISPLAY=':10.0' python3 -m unittest test.test_main
# name **må** være pages
pages:
stage: deploy
- image: morkolai/paa-bittet-ci
+ image: morkolai/soitool-ci
script:
- mkdir public
- - pip install pdoc3 pyside2
- pdoc --version
- pdoc soitool --html
- mv ./html/soitool/* ./public
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000000000000000000000000000000000000..e1cd2b012ede6a78fc06c7a1fc7e2889368f5ce6
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,27 @@
+FROM ubuntu:16.04
+
+# This Dockerfile describes the container used in .gitlab-ci.yml
+
+# Need docker build arg, such as --build-arg FLAKE8_VERSION=3.0.4
+ARG FLAKE8_VERSION=3.7
+
+# Installing python3.7
+RUN apt-get update
+RUN apt-get install software-properties-common -y
+RUN add-apt-repository ppa:deadsnakes/ppa -y
+RUN apt-get update
+RUN apt-get install python3.7 -y
+RUN python3.7 -V
+RUN apt-get install python3-pip -y
+RUN pip3 install --upgrade pip
+
+# To make -platform offscreen,minimal,vnc work
+RUN apt-get install -y libegl1-mesa-dev \
+ libfontconfig1-dev \
+ libsdl1.2-dev \
+ libwayland-dev \
+ libxkbcommon-dev
+
+WORKDIR /code
+COPY requirements.txt requirements.txt
+RUN pip3 install -r requirements.txt
diff --git a/README.md b/README.md
index 874486f50bd3b384696d0140027f4c9f74ce67fa..b7538b1afdab2a83a1990e9b1a4abfe14de44e56 100644
--- a/README.md
+++ b/README.md
@@ -51,4 +51,44 @@ Gjøres med pdoc3:
* `pdoc3 --html --output-dir .\docs .\soitool\main.py`
-* Uten kildekode: `pdoc3 --html --config show_source_code=False --output-dir .\docs\ .\soitool\main.py`
\ No newline at end of file
+* Uten kildekode: `pdoc3 --html --config show_source_code=False --output-dir .\docs\ .\soitool\main.py`
+
+## Om `Dockerfile`
+
+Docker image som brukes i `.gitlab-ci.yml` er bygget med filen `Dockerfile` og er lastet opp som `morkolai/soitool-ci`. Docker image inneholder alle avhengigheter til prosjektet. Følgende prosedyre brukes for å oppdatere image. Dette må gjøres når `requirements.txt` endrer seg.
+
+```bash
+docker build -t morkolai/soitool-ci .
+docker login
+docker push morkolai/soitool-ci
+```
+
+## Arbeidsmetode - Bruk av `git`
+
+Arbeid skal ikke skje direkte på `master` branch. For hver oppgave en vil utføre skal en ny branch lages, og denne må senere merges inn ved hjelp av en "Merge Request". Gjennomgang av dette er lagt fram under:
+
+```bash
+# ny branch
+git branch <branch navn>
+# hoppe til eksisterende branch
+git checkout <branch navn>
+# ..jobb med koden..
+git add <...>
+git commit -m "..."
+# push til gitlab
+git push origin <branch navn>
+```
+
+Merging til master skal skje via Merge Requests i GitLab.
+
+### Om arbeid utføres på feil branch
+
+`git stash` kan brukes for å lagre endringer i et "stash". Deretter kan en hoppe til riktig branch med `git checkout <branch navn>`, og kjøre `git stash pop`.
+
+## Arbeidsmetode - Hvordan skrive tester
+
+Hver modul burde testes. I praksis vil dette si at hver fil under `soitool/` med navn `X.py` burde ha en tilsvarende fil under `test/` med navn `test_X.py`.
+
+Ved GUI-testing av modulære dialoger (dialoger som stopper eksekvering av hovedvindu) foretrekkes fremgangsmåten som demonstreres i `test\test_main.py`, hvor testfunksjoner kjøres med `singleShot`. Antall millisekunder testen skal vente før testfunksjoner kjøres kan stilles inn for tregere maskiner.
+
+*TODO* hvordan skrive GUI tester.
diff --git a/requirements.txt b/requirements.txt
index e7003db1ed313fd7ea05e86259946a579bafc7f9..c3395b573911f97cc9e70e8baee6e4eb101d7899 100644
Binary files a/requirements.txt and b/requirements.txt differ
diff --git a/scripts/.flake8 b/scripts/.flake8
new file mode 100644
index 0000000000000000000000000000000000000000..b67c07910db8b368186b279e7ae62434899ff202
--- /dev/null
+++ b/scripts/.flake8
@@ -0,0 +1,5 @@
+[flake8]
+# ignorerer sjekk for lange linjer, ettersom dette gjøres i pylint
+# vi vil tillate lange linjer som avsluttes med link, og dette er mulig i
+# pylint, men såvidt vi vet ikke mulig i flake8
+ignore = E501
diff --git a/scripts/.pylintrc b/scripts/.pylintrc
index 7e9ef2c59e6d2485745e4bb9538672918024a7cb..43b2b9bbb126b33d46a4ce3faab76ffb6c1b2b72 100644
--- a/scripts/.pylintrc
+++ b/scripts/.pylintrc
@@ -275,7 +275,7 @@ redefining-builtins-modules=six.moves,past.builtins,future.builtins,builtins,io
expected-line-ending-format=
# Regexp for a line that is allowed to be longer than the limit.
-ignore-long-lines=^\s*(# )?<?https?://\S+>?$
+ignore-long-lines=^.*<?https?://\S+>?$
# Number of spaces of indent required inside a hanging or continued line.
indent-after-paren=4
diff --git a/test/__init__.py b/test/__init__.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..5f1ae119d6f959cc9e9b1a127e305d5145dc620e 100644
--- a/test/__init__.py
+++ b/test/__init__.py
@@ -0,0 +1 @@
+"""Tester koden vår."""
diff --git a/test/test_main.py b/test/test_main.py
index 8f422d611bdb75722b1adefa7388d81cef5e74b4..beadccfe5991936ceba67b155d69db315ddf1414 100644
--- a/test/test_main.py
+++ b/test/test_main.py
@@ -1,7 +1,10 @@
+"""Test CoolWidget."""
+
import unittest
import sys
+from datetime import datetime as datetime_, timedelta
+from PySide2 import QtWidgets, QtTest, QtCore
from soitool import main
-from PySide2 import QtGui, QtWidgets, QtTest, QtCore
# references:
# * findChild: https://srinikom.github.io/pyside-docs/PySide/QtCore/QObject.html#PySide.QtCore.PySide.QtCore.QObject.findChild
@@ -18,26 +21,42 @@ from PySide2 import QtGui, QtWidgets, QtTest, QtCore
# moved here from setUp to avoid annoying startup messages
app = QtWidgets.QApplication(sys.argv)
+
def wait(msec):
- QtTest.QTest.qWait(msec)
+ """Venter msec millisekunder.
+
+ Bruker https://stackoverflow.com/a/34745326/3545896
+
+ Parameter
+ -----
+ msec : number
+ msec to wait
+ """
+ end = datetime_.now() + timedelta(milliseconds=msec)
+ while datetime_.now() < end:
+ app.processEvents()
+
class TestMain(unittest.TestCase):
+ """TestCase for main."""
def setUp(self):
+ """Forbereder widget for testing."""
self.test_text1 = "A bad boy"
self.test_text2 = "Hei Anders!"
self.widget = main.CoolWidget(self.test_text1)
self.widget.show()
def test_starts_up(self):
+ """Test at widget kan starte opp."""
self.assertEqual(
- self.widget.qlabel.text(),
- self.test_text1,
+ self.widget.qlabel.text(),
+ self.test_text1,
)
self.assertTrue(self.widget.isVisible())
-
- # This test shows how waiting for a referenced dialog can create more reliable tests. The only downside is having to ensure that the mouseClick after the singleShot is allowed to run. If we put a singleShot delay too short the line is never allowed to execute, and the while-loop of the inner function takes over the whole world
+
def test_change_text_ok(self):
+ """Test at endring av tekst funker."""
def change_text_and_ok():
while self.widget.dlg_input is None:
@@ -46,7 +65,6 @@ class TestMain(unittest.TestCase):
QtTest.QTest.keyClicks(child_line_edit, self.test_text2)
QtTest.QTest.keyClick(child_line_edit, QtCore.Qt.Key_Enter)
- # delay of 100 to allow the next line to execute
QtCore.QTimer.singleShot(100, change_text_and_ok)
QtTest.QTest.mouseClick(self.widget.button, QtCore.Qt.LeftButton)
@@ -54,15 +72,17 @@ class TestMain(unittest.TestCase):
self.widget.qlabel.text(),
self.test_text2,
)
-
- # This test shows how we can use singleShot to trick code to be ran after .exec() of a dialog. The downside is that for slow computers the singleShot could be ran before .exec(). See the above test for an example approach to avoid this. A boon of this method is that we don't need to test against stored references to dialogs, we can instead rely on app.activeModalWidget()
+
def test_change_text_not_ok(self):
+ """Test at avbrytelse av endring av tekster ikke endrer tekst."""
def accept_popup():
QtTest.QTest.keyClick(app.activeModalWidget(), QtCore.Qt.Key_Enter)
def change_text_and_not_ok():
- # in PySide2 we need to store a reference to this. If we don't the widget is garbage collected somehow before we get to use child_line_edit (a child of the active widget)
+ # in PySide2 we need to store a reference to this. If we don't the
+ # widget is garbage collected somehow before we get to use
+ # child_line_edit (a child of the active widget)
active_widget = app.activeModalWidget()
child_line_edit = active_widget.findChild(QtWidgets.QLineEdit)
@@ -73,11 +93,12 @@ class TestMain(unittest.TestCase):
QtCore.QTimer.singleShot(0, change_text_and_not_ok)
QtTest.QTest.mouseClick(self.widget.button, QtCore.Qt.LeftButton)
-
+
self.assertNotEqual(
self.widget.qlabel.text(),
self.test_text2,
)
+
if __name__ == '__main__':
unittest.main()