Skip to content

Commit

Permalink
Merge pull request #20 from yakimka/add_tests
Browse files Browse the repository at this point in the history
Add tests
  • Loading branch information
yakimka authored Oct 8, 2020
2 parents 72f6c21 + 5cfd024 commit 8a19e0c
Show file tree
Hide file tree
Showing 15 changed files with 1,383 additions and 70 deletions.
10 changes: 5 additions & 5 deletions CherryTomato/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,25 +18,25 @@
logFmt = '%(asctime)s:%(levelname)s:%(name)s:%(message)s'
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(CompactFormatter(logFmt, "%Y-%m-%d %H:%M:%S"))
logger = makeLogger('CherryTomato', handler=handler, level='INFO')
logger = makeLogger(None, handler=handler, level='INFO')


def setTrayIcon(app, mainWindow):
app.setQuitOnLastWindowClosed(False)
icon = QIcon(APP_ICON)
trayIcon = QSystemTrayIcon(parent=app, icon=icon)
trayIcon = QSystemTrayIcon(parent=mainWindow, icon=icon)

menu = QMenu(parent=mainWindow)

settingsAction = QAction(text='Settings', parent=menu)
settingsAction = QAction(text='Settings', parent=mainWindow)
settingsAction.triggered.connect(mainWindow.showSettingsWindow)
menu.addAction(settingsAction)

aboutAction = QAction(text='About', parent=menu)
aboutAction = QAction(text='About', parent=mainWindow)
aboutAction.triggered.connect(mainWindow.showAboutWindow)
menu.addAction(aboutAction)

quitAction = QAction(text='Quit', parent=menu)
quitAction = QAction(text='Quit', parent=mainWindow)
quitAction.triggered.connect(app.quit)
menu.addAction(quitAction)

Expand Down
2 changes: 1 addition & 1 deletion CherryTomato/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,7 @@ def __getattr__(self, item):
opt = self._findOption(item)
if opt:
return self.settingsBackend.value(opt.name, opt.default, **opt.getTypeKwarg())
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}")
raise AttributeError(f"'{self.__class__.__name__}' object has no attribute '{item}'")

def _findOption(self, name):
return next((opt for opt in self.options if opt.name == name), None)
Expand Down
8 changes: 6 additions & 2 deletions CherryTomato/widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,12 @@ def calculateInnerRect(self, outerRadius: float):
return firstInnerRect, secondInnerRect, innerRadius

def drawTwoTexts(
self, p: QPainter, firstRect: QRectF, secondRect: QRectF, innerRadius: float,
value: float
self,
p: QPainter,
firstRect: QRectF,
secondRect: QRectF,
innerRadius: float,
value: float,
):
if not self.m_format:
return
Expand Down
16 changes: 12 additions & 4 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,23 @@ def default_settings():
'stateTomato': 100,
'stateBreak': 25,
'stateLongBreak': 50,
'repeat': 4,
'autoStopTomato': False,
'autoStopBreak': False,
'switchToTomatoOnAbort': True,

'size': QSize(400, 520),
'position': QPoint(50, 50),

'useSystemFont': True,
'showTrayIcon': True,
'notification': True,
'interrupt': True,
'repeat': 4,
'autoStopTomato': False,
'autoStopBreak': False,
'switchToTomatoOnAbort': True,

'afterStartCommand': 'echo after_start',
'afterStopCommand': 'echo after_stop',
'onTomatoCommand': 'echo on_tomato',
'onBreakCommand': 'echo on_break',
}


Expand Down
51 changes: 51 additions & 0 deletions tests/test_main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import pytest

from CherryTomato import main

pytestmark = pytest.mark.usefixtures('mock_objects')


@pytest.fixture
def mock_objects(mocker):
mocker.patch.object(main, 'QIcon')
mocker.patch.object(main, 'QSystemTrayIcon')
mocker.patch.object(main, 'QMenu')
mocker.patch.object(main, 'QAction')
mocker.patch.object(main, 'QCoreApplication')
mocker.patch.object(main, 'Qt')
mocker.patch.object(main, 'CherryTomatoSettings')
mocker.patch.object(main, 'addCustomFont')
mocker.patch.object(main, 'TomatoTimer')
mocker.patch.object(main, 'TomatoTimerProxy')
mocker.patch.object(main, 'CommandExecutor')
mocker.patch.object(main, 'CherryTomatoMainWindow')


def test_set_tray_icon():
main.main()

main.QSystemTrayIcon.assert_called()


def test_not_set_tray_icon():
main.CherryTomatoSettings.createQT().showTrayIcon = False

main.main()

main.QSystemTrayIcon.assert_not_called()


def test_use_system_font():
main.CherryTomatoSettings.createQT().useSystemFont = True

main.main()

main.addCustomFont.assert_not_called()


def test_not_use_system_font():
main.CherryTomatoSettings.createQT().useSystemFont = True

main.main()

main.QSystemTrayIcon.assert_called()
182 changes: 182 additions & 0 deletions tests/test_qroundprogressbar.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
from unittest import mock
from unittest.mock import Mock

import pytest
from PyQt5.QtCore import QRectF

from CherryTomato.widget import QRoundProgressBar, QRoundProgressBar_


@pytest.fixture
def mock_qroundprogressbar_(mocker):
mocker.patch('CherryTomato.widget.QRoundProgressBar_.__init__')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.setFormat')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.setValue')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.setBarStyle')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.setOutlinePenWidth')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.setDataPenWidth')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.setMaximumWidth')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.width')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.height')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.valueFormatChanged')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.rebuildDataBrushIfNeeded')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.drawBackground')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.drawBase')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.drawValue')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.drawInnerBackground')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.palette')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.calculateInnerRect')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.font')
mocker.patch('CherryTomato.widget.QRoundProgressBar_.valueToText')


@pytest.fixture
def mock_qpainter(mocker):
return mocker.patch('CherryTomato.widget.QPainter')


@pytest.fixture
def mock_qfontmetricsf(mocker):
return mocker.patch('CherryTomato.widget.QFontMetricsF')


@pytest.fixture
def mock_qfont(mocker):
return mocker.patch('CherryTomato.widget.QFont')


@pytest.fixture
def mock_qrectf(mocker):
return mocker.patch('CherryTomato.widget.QRectF')


@pytest.fixture
def progressbar(mock_qroundprogressbar_, mock_qpainter):
progressbar = QRoundProgressBar('parent')
progressbar.m_value = 0
progressbar.m_min = 0
progressbar.m_max = 0
return progressbar


def test_init(progressbar):
QRoundProgressBar_.__init__.assert_called_once_with('parent')
assert progressbar.useSystemFont is True
assert '' == progressbar.m_second_format
progressbar.setFormat.assert_called_once_with('')
progressbar.setValue.assert_called_once_with(0)
progressbar.setBarStyle.assert_called_once_with(QRoundProgressBar.BarStyle.LINE)
progressbar.setOutlinePenWidth.assert_called_once_with(4)
progressbar.setDataPenWidth.assert_called_once_with(4)


def test_resizeEvent(progressbar):
progressbar.resizeEvent(Mock())

progressbar.height.assert_called_once_with()
progressbar.setMaximumWidth.assert_called_once_with(progressbar.height())


def test_setSecondFormat(progressbar):
progressbar.setSecondFormat('new_format')

assert 'new_format' == progressbar.m_second_format
progressbar.valueFormatChanged.assert_called_once_with()


def test_setSecondFormat_same_value(progressbar):
progressbar.m_second_format = 'new_format'
progressbar.setSecondFormat('new_format')

progressbar.valueFormatChanged.assert_not_called()


@pytest.fixture
def mock_for_paintEvent(mocker, progressbar):
progressbar.width.return_value = 100
progressbar.height.return_value = 200
mocker.patch('CherryTomato.widget.QRoundProgressBar.drawTwoTexts')
mocker.patch('CherryTomato.widget.QRoundProgressBar.calculateInnerRect',
return_value=('firstInnerRect', 'secondInnerRect', 'innerRadius'))


@pytest.mark.parametrize('width,height', [
(100, 200),
(200, 100),
])
def test_paintEvent(progressbar, mock_for_paintEvent, width, height):
progressbar.width.return_value = width
progressbar.height.return_value = height

progressbar.paintEvent(Mock())

progressbar.rebuildDataBrushIfNeeded.assert_called_once_with()
progressbar.drawBackground.assert_called()
progressbar.drawBase(mock.ANY, QRectF(1.0, 1.0, 98.0, 98.0))
progressbar.drawValue.assert_called_once_with(mock.ANY, mock.ANY, 0, 0)
assert 2 == progressbar.drawInnerBackground.call_count
progressbar.drawInnerBackground.assert_any_call(mock.ANY, 'secondInnerRect')
progressbar.drawInnerBackground.assert_any_call(mock.ANY, 'firstInnerRect')
progressbar.drawTwoTexts.assert_called_once_with(
mock.ANY,
'firstInnerRect',
'secondInnerRect',
'innerRadius',
0,
)


@pytest.mark.parametrize('m_value,m_max,m_min,expected_delta', [
(0, 100, 200, 0),
(1, 100, 201, 0.505),
(10, 500, 260, -0.96),
])
def test_paintEvent_delta(progressbar, mock_for_paintEvent, m_value, m_max, m_min, expected_delta):
progressbar.m_value = m_value
progressbar.m_max = m_max
progressbar.m_min = m_min
progressbar.paintEvent(Mock())

progressbar.drawValue.assert_called_once_with(mock.ANY, mock.ANY, m_value, expected_delta)


def test_calculateInnerRect(progressbar):
minnerRect = Mock()
minnerRect.getRect.return_value = (100, 500, 200, 300)
QRoundProgressBar_.calculateInnerRect.return_value = (minnerRect, 20)

firstHeight = 300 * 0.7
expected_first = QRectF(100, 500, 200, firstHeight)
expected_second = QRectF(100, 500 + firstHeight, 200, 300 - firstHeight)
assert (expected_first, expected_second, 20) == progressbar.calculateInnerRect(12.5)
QRoundProgressBar_.calculateInnerRect.assert_called_once_with(12.5)


def test_drawTwoTexts_not_executed(progressbar, mock_qfont):
progressbar.m_format = 0

progressbar.drawTwoTexts(Mock(), Mock(), Mock(), 20, 21)

mock_qfont().setPixelSize.assert_not_called()


@pytest.mark.parametrize('use_system_font,expected', [
(True, 'SystemFont'),
(False, 'Noto Sans'),
])
def test_drawTwoTexts_system_font(
progressbar,
mock_qfontmetricsf,
mock_qrectf,
mock_qfont,
use_system_font,
expected,
):
progressbar.font = Mock(return_value='SystemFont')
progressbar.m_format = 1
progressbar.useSystemFont = use_system_font

progressbar.drawTwoTexts(Mock(), Mock(), Mock(), 20, 21)

mock_qfont.assert_called_once_with(expected)
mock_qfont().setPixelSize.assert_called()
76 changes: 76 additions & 0 deletions tests/test_qroundpushbutton.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
from unittest.mock import Mock

import pytest
from PyQt5 import QtCore

from CherryTomato.widget import QRoundPushbutton, QPushButton


@pytest.fixture
def mock_qpushbutton(mocker):
mocker.patch('CherryTomato.widget.QPushButton.__init__')
mocker.patch('CherryTomato.widget.QPushButton.setFixedSize')
mocker.patch('CherryTomato.widget.QPushButton.setFlat')
mocker.patch('CherryTomato.widget.QPushButton.setAttribute')
mocker.patch('CherryTomato.widget.QPushButton.setStyleSheet')


@pytest.fixture
def button(mock_qpushbutton):
return QRoundPushbutton('parent')


def test_init(button):
QPushButton.__init__.assert_called_once_with(parent='parent')
button.setFixedSize.assert_called_once_with(40, 40)
button.setFlat.assert_called_once_with(True)
button.setAttribute.assert_called_once_with(QtCore.Qt.WA_TranslucentBackground)
assert (255, 37, 51) == button.color
assert button.image.endswith('/CherryTomato/media/play.png')
button.setStyleSheet.assert_called()


def test_setColor(button):
mapplyStyleSheet = Mock()
button.applyStyleSheet = mapplyStyleSheet

button.setColor((101, 102, 103))

assert (101, 102, 103) == button.color
mapplyStyleSheet.assert_called_once_with()


def test_setImage(button):
mapplyStyleSheet = Mock()
button.applyStyleSheet = mapplyStyleSheet

button.setImage('button.png')

assert button.image.endswith('/CherryTomato/media/button.png')
mapplyStyleSheet.assert_called_once_with()


def test_setImage_called_twice(button):
mapplyStyleSheet = Mock()
button.applyStyleSheet = mapplyStyleSheet

button.setImage('button.png')
button.setImage('button.png')

assert button.image.endswith('/CherryTomato/media/button.png')
mapplyStyleSheet.assert_called_once_with()


def test_applyStyleSheet(button):
button.color = (101, 102, 103)
button.image = 'button.png'

button.setStyleSheet.reset_mock()
button.applyStyleSheet()

button.setStyleSheet.assert_called_once_with('''
background-color: rgb(101, 102, 103);
background-image: url("button.png");
border: 1px solid rgb(101, 102, 103);
border-radius: 20px;
''')
Loading

0 comments on commit 8a19e0c

Please sign in to comment.