Skip to content

Commit cdb436f

Browse files
committed
More Qt tests
1 parent e361dba commit cdb436f

5 files changed

Lines changed: 254 additions & 5 deletions

File tree

src/bitmessageqt/tests/__init__.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@
44
from .main import TestMain, TestUISignaler
55
from .settings import TestSettings
66
from .support import TestSupport
7+
from .test_import import TestImports
8+
from .test_startup import TestStartup
9+
from .test_widgets import (
10+
TestAddressValidator, TestLanguageBox, TestMessageView,
11+
TestSafeHTMLParser
12+
)
713

814
__all__ = [
9-
"TestAddressbook", "TestMain", "TestSettings", "TestSupport",
10-
"TestUISignaler"
15+
"TestAddressbook", "TestAddressValidator", "TestLanguageBox",
16+
"TestImports", "TestMain", "TestMessageView", "TestSafeHTMLParser",
17+
"TestSettings", "TestStartup", "TestSupport", "TestUISignaler"
1118
]

src/bitmessageqt/tests/main.py

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,18 +10,47 @@
1010
from bitmessageqt import _translate, config, queues
1111

1212

13+
# pylint: disable=too-few-public-methods
14+
class TestApp(QtGui.QApplication):
15+
"""Lightweight QApplication subclass for tests, without the heavy
16+
BitmessageQtApplication init (QLocalSocket singleton check,
17+
organisation metadata, etc.)."""
18+
19+
@staticmethod
20+
def get_windowstyle():
21+
"""Get window style set in config or default"""
22+
return config.safeGet(
23+
'bitmessagesettings', 'windowstyle',
24+
'Windows' if sys.platform.startswith('win') else 'GTK+'
25+
)
26+
27+
28+
def get_test_app():
29+
"""Return the existing QApplication or create a TestApp.
30+
31+
If the running app is a plain QApplication (missing get_windowstyle),
32+
patch in the required methods from TestApp."""
33+
app = QtGui.QApplication.instance()
34+
if app is None:
35+
return TestApp(sys.argv)
36+
if not hasattr(app, 'get_windowstyle'):
37+
# Bolt on the methods the tests expect; this happens when
38+
# another test already created a bare QApplication.
39+
app.get_windowstyle = TestApp.get_windowstyle
40+
return app
41+
42+
1343
class TestBase(unittest.TestCase):
1444
"""Base class for bitmessageqt test case"""
1545

1646
@classmethod
1747
def setUpClass(cls):
1848
"""Provide the UI test cases with common settings"""
1949
cls.config = config
50+
cls.app = get_test_app()
2051

2152
def setUp(self):
22-
self.app = (
23-
QtGui.QApplication.instance()
24-
or bitmessageqt.BitmessageQtApplication(sys.argv))
53+
self.app = self.__class__.app
2554
self.window = self.app.activeWindow()
2655
if not self.window:
2756
self.window = bitmessageqt.MyForm()
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
"""
2+
Smoke tests: verify that bitmessageqt modules can be imported.
3+
4+
These tests require PyQt4 to be installed but do NOT need a running
5+
X server, database, or any bitmessage backend threads.
6+
"""
7+
import unittest
8+
9+
10+
# pylint: disable=import-error,unused-variable
11+
class TestImports(unittest.TestCase):
12+
"""Verify that key bitmessageqt modules are importable"""
13+
14+
@staticmethod
15+
def test_import_bitmessageqt():
16+
"""The main bitmessageqt package should be importable"""
17+
import bitmessageqt
18+
19+
@staticmethod
20+
def test_import_bitmessageui():
21+
"""The generated UI module should be importable"""
22+
from bitmessageqt import bitmessageui
23+
24+
@staticmethod
25+
def test_import_settings():
26+
"""The settings dialog module should be importable"""
27+
from bitmessageqt import settings
28+
29+
@staticmethod
30+
def test_import_address_dialogs():
31+
"""The address dialogs module should be importable"""
32+
from bitmessageqt import address_dialogs
33+
34+
@staticmethod
35+
def test_import_networkstatus():
36+
"""The network status module should be importable"""
37+
from bitmessageqt import networkstatus
38+
39+
@staticmethod
40+
def test_import_safehtmlparser():
41+
"""safehtmlparser should be importable"""
42+
from bitmessageqt import safehtmlparser
43+
44+
@staticmethod
45+
def test_import_support():
46+
"""The support module should be importable"""
47+
from bitmessageqt import support
48+
49+
@staticmethod
50+
def test_import_foldertree():
51+
"""The foldertree module should be importable"""
52+
from bitmessageqt import foldertree
53+
54+
@staticmethod
55+
def test_import_messageview():
56+
"""The messageview module should be importable"""
57+
from bitmessageqt import messageview
58+
59+
@staticmethod
60+
def test_import_utils():
61+
"""The utils module should be importable"""
62+
from bitmessageqt import utils
63+
64+
@staticmethod
65+
def test_import_account():
66+
"""The account module should be importable"""
67+
from bitmessageqt import account
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""
2+
Smoke test: verify the main window can be created and shut down.
3+
4+
This test requires the full bitmessage backend to be running
5+
(it is designed to run via ``bitmessagemain.py -t`` or from
6+
``src/tests/core.py``). It also needs a display (Xvfb is fine).
7+
"""
8+
import sys
9+
import unittest
10+
11+
try:
12+
from PyQt4 import QtCore, QtGui
13+
has_qt = True
14+
except ImportError:
15+
has_qt = False
16+
17+
18+
@unittest.skipUnless(has_qt, "requires PyQt4")
19+
class TestStartup(unittest.TestCase):
20+
"""Verify the main window starts and has expected structure"""
21+
22+
def setUp(self):
23+
import bitmessageqt
24+
self.app = (
25+
QtGui.QApplication.instance()
26+
or bitmessageqt.BitmessageQtApplication(sys.argv))
27+
self.window = self.app.activeWindow()
28+
if not self.window:
29+
self.window = bitmessageqt.MyForm()
30+
self.window.appIndicatorInit(self.app)
31+
32+
def test_window_exists(self):
33+
"""The main window should be created successfully"""
34+
self.assertIsNotNone(self.window)
35+
self.assertIsNotNone(self.window.ui)
36+
37+
def test_window_has_tabs(self):
38+
"""The main window should have the expected tab widget"""
39+
tabs = self.window.ui.tabWidget
40+
self.assertIsNotNone(tabs)
41+
self.assertGreater(tabs.count(), 0)
42+
43+
def test_window_title(self):
44+
"""The main window should have a non-empty title"""
45+
self.assertTrue(len(self.window.windowTitle()) > 0)
46+
47+
def test_status_bar(self):
48+
"""The main window should have a status bar"""
49+
self.assertIsNotNone(self.window.statusBar())
50+
51+
def test_quit_cycle(self):
52+
"""The event loop should start and stop without crashing"""
53+
QtCore.QTimer.singleShot(50, self.app.quit)
54+
self.app.exec_()
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
"""
2+
Unit tests for individual bitmessageqt widgets.
3+
4+
These tests need a display (real or virtual via Xvfb/xvfb-run) and PyQt4,
5+
but do NOT require the full bitmessage backend, database, or network.
6+
Each test creates only the minimal widget under test.
7+
"""
8+
import sys
9+
import unittest
10+
11+
try:
12+
from PyQt4 import QtCore, QtGui, QtTest
13+
has_qt = True
14+
except ImportError:
15+
has_qt = False
16+
17+
18+
def get_app():
19+
"""Return existing QApplication or create a new one"""
20+
return QtGui.QApplication.instance() or QtGui.QApplication(sys.argv)
21+
22+
23+
@unittest.skipUnless(has_qt, "requires PyQt4")
24+
class TestSafeHTMLParser(unittest.TestCase):
25+
"""Test the SafeHTMLParser used for message rendering"""
26+
27+
def test_unescape(self):
28+
"""Undo urlencoding"""
29+
from bitmessageqt.safehtmlparser import SafeHTMLParser
30+
parser = SafeHTMLParser()
31+
parser.reset()
32+
parser.reset_safe()
33+
parser.feed("<b>hello</b> &amp; world")
34+
self.assertIn("hello", parser.sanitised)
35+
self.assertNotIn("<script", parser.sanitised)
36+
37+
38+
@unittest.skipUnless(has_qt, "requires PyQt4")
39+
class TestMessageView(unittest.TestCase):
40+
"""Test the MessageView widget in isolation"""
41+
42+
def setUp(self):
43+
self.app = get_app()
44+
45+
def test_create_messageview(self):
46+
"""MessageView widget can be instantiated"""
47+
from bitmessageqt.messageview import MessageView
48+
widget = MessageView(None)
49+
self.assertIsNotNone(widget)
50+
51+
@staticmethod
52+
def test_messageview_set_content():
53+
"""MessageView.setContent should not crash"""
54+
from bitmessageqt.messageview import MessageView
55+
widget = MessageView(None)
56+
widget.setContent("Hello, this is a <b>test</b> message.")
57+
58+
59+
@unittest.skipUnless(has_qt, "requires PyQt4")
60+
class TestAddressValidator(unittest.TestCase):
61+
"""Test the AddressValidator"""
62+
63+
def setUp(self):
64+
self.app = get_app()
65+
66+
def test_create_validator(self):
67+
"""AddressValidator can be instantiated with a buttonBox"""
68+
from bitmessageqt.addressvalidator import AddressValidator
69+
line_edit = QtGui.QLineEdit()
70+
# AddressValidator.setParams() reads the Ok button label on init,
71+
# so a real QDialogButtonBox is the simplest way to satisfy that.
72+
button_box = QtGui.QDialogButtonBox(QtGui.QDialogButtonBox.Ok)
73+
validator = AddressValidator(
74+
line_edit, buttonBox=button_box)
75+
self.assertIsNotNone(validator)
76+
self.assertFalse(validator.isValid)
77+
78+
79+
@unittest.skipUnless(has_qt, "requires PyQt4")
80+
class TestLanguageBox(unittest.TestCase):
81+
"""Test the language selection combobox"""
82+
83+
def setUp(self):
84+
self.app = get_app()
85+
86+
def test_create_languagebox(self):
87+
"""LanguageBox can be instantiated"""
88+
from bitmessageqt.languagebox import LanguageBox
89+
parent = QtGui.QWidget()
90+
combo = LanguageBox(parent)
91+
self.assertIsNotNone(combo)
92+
self.assertGreater(combo.count(), 0)

0 commit comments

Comments
 (0)