From 66aea7146853ac9941f2178d2d08cd72fefa6e8b Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Sun, 3 Nov 2019 00:47:51 -0700 Subject: [PATCH] Start writing tabs tests --- onionshare/common.py | 29 ++--- onionshare_gui/tab/tab.py | 28 +++-- tests2/__init__.py | 0 tests2/conftest.py | 192 +++++++++++++++++++++++++++++++ tests2/test_tabs.py | 231 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 455 insertions(+), 25 deletions(-) create mode 100644 tests2/__init__.py create mode 100644 tests2/conftest.py create mode 100644 tests2/test_tabs.py diff --git a/onionshare/common.py b/onionshare/common.py index 5245ddf9..cf713818 100644 --- a/onionshare/common.py +++ b/onionshare/common.py @@ -159,20 +159,23 @@ class Common: """ Returns the path of the OnionShare data directory. """ - if self.platform == "Windows": - try: - appdata = os.environ["APPDATA"] - onionshare_data_dir = f"{appdata}\\OnionShare" - except: - # If for some reason we don't have the 'APPDATA' environment variable - # (like running tests in Linux while pretending to be in Windows) - onionshare_data_dir = os.path.expanduser("~/.config/onionshare") - elif self.platform == "Darwin": - onionshare_data_dir = os.path.expanduser( - "~/Library/Application Support/OnionShare" - ) + if getattr(sys, "onionshare_test_mode", False): + onionshare_data_dir = os.path.expanduser("~/.config/onionshare-testdata") else: - onionshare_data_dir = os.path.expanduser("~/.config/onionshare") + if self.platform == "Windows": + try: + appdata = os.environ["APPDATA"] + onionshare_data_dir = f"{appdata}\\OnionShare" + except: + # If for some reason we don't have the 'APPDATA' environment variable + # (like running tests in Linux while pretending to be in Windows) + onionshare_data_dir = os.path.expanduser("~/.config/onionshare") + elif self.platform == "Darwin": + onionshare_data_dir = os.path.expanduser( + "~/Library/Application Support/OnionShare" + ) + else: + onionshare_data_dir = os.path.expanduser("~/.config/onionshare") os.makedirs(onionshare_data_dir, 0o700, True) return onionshare_data_dir diff --git a/onionshare_gui/tab/tab.py b/onionshare_gui/tab/tab.py index cf648d8c..53dc4ad5 100644 --- a/onionshare_gui/tab/tab.py +++ b/onionshare_gui/tab/tab.py @@ -67,23 +67,27 @@ class Tab(QtWidgets.QWidget): self.app = OnionShare(common, self.common.gui.onion, self.common.gui.local_only) # Widgets to display on a new tab - share_button = QtWidgets.QPushButton(strings._("gui_new_tab_share_button")) - share_button.setStyleSheet(self.common.gui.css["mode_new_tab_button"]) + self.share_button = QtWidgets.QPushButton(strings._("gui_new_tab_share_button")) + self.share_button.setStyleSheet(self.common.gui.css["mode_new_tab_button"]) share_description = QtWidgets.QLabel(strings._("gui_new_tab_share_description")) share_description.setWordWrap(True) - share_button.clicked.connect(self.share_mode_clicked) + self.share_button.clicked.connect(self.share_mode_clicked) - receive_button = QtWidgets.QPushButton(strings._("gui_new_tab_receive_button")) - receive_button.setStyleSheet(self.common.gui.css["mode_new_tab_button"]) - receive_button.clicked.connect(self.receive_mode_clicked) + self.receive_button = QtWidgets.QPushButton( + strings._("gui_new_tab_receive_button") + ) + self.receive_button.setStyleSheet(self.common.gui.css["mode_new_tab_button"]) + self.receive_button.clicked.connect(self.receive_mode_clicked) receive_description = QtWidgets.QLabel( strings._("gui_new_tab_receive_description") ) receive_description.setWordWrap(True) - website_button = QtWidgets.QPushButton(strings._("gui_new_tab_website_button")) - website_button.setStyleSheet(self.common.gui.css["mode_new_tab_button"]) - website_button.clicked.connect(self.website_mode_clicked) + self.website_button = QtWidgets.QPushButton( + strings._("gui_new_tab_website_button") + ) + self.website_button.setStyleSheet(self.common.gui.css["mode_new_tab_button"]) + self.website_button.clicked.connect(self.website_mode_clicked) website_description = QtWidgets.QLabel( strings._("gui_new_tab_website_description") ) @@ -91,13 +95,13 @@ class Tab(QtWidgets.QWidget): new_tab_layout = QtWidgets.QVBoxLayout() new_tab_layout.addStretch(1) - new_tab_layout.addWidget(share_button) + new_tab_layout.addWidget(self.share_button) new_tab_layout.addWidget(share_description) new_tab_layout.addSpacing(50) - new_tab_layout.addWidget(receive_button) + new_tab_layout.addWidget(self.receive_button) new_tab_layout.addWidget(receive_description) new_tab_layout.addSpacing(50) - new_tab_layout.addWidget(website_button) + new_tab_layout.addWidget(self.website_button) new_tab_layout.addWidget(website_description) new_tab_layout.addStretch(3) diff --git a/tests2/__init__.py b/tests2/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/tests2/conftest.py b/tests2/conftest.py new file mode 100644 index 00000000..8d8e77f6 --- /dev/null +++ b/tests2/conftest.py @@ -0,0 +1,192 @@ +import sys + +# Force tests to look for resources in the source code tree +sys.onionshare_dev_mode = True + +# Let OnionShare know the tests are running, to avoid colliding with settings files +sys.onionshare_test_mode = True + +import os +import shutil +import tempfile + +import pytest + +from onionshare import common, web, settings, strings + + +def pytest_addoption(parser): + parser.addoption( + "--rungui", action="store_true", default=False, help="run GUI tests" + ) + parser.addoption( + "--runtor", action="store_true", default=False, help="run tor tests" + ) + + +def pytest_collection_modifyitems(config, items): + if not config.getoption("--runtor"): + # --runtor given in cli: do not skip tor tests + skip_tor = pytest.mark.skip(reason="need --runtor option to run") + for item in items: + if "tor" in item.keywords: + item.add_marker(skip_tor) + + if not config.getoption("--rungui"): + # --rungui given in cli: do not skip GUI tests + skip_gui = pytest.mark.skip(reason="need --rungui option to run") + for item in items: + if "gui" in item.keywords: + item.add_marker(skip_gui) + + +@pytest.fixture +def temp_dir_1024(): + """ Create a temporary directory that has a single file of a + particular size (1024 bytes). + """ + + tmp_dir = tempfile.mkdtemp() + tmp_file, tmp_file_path = tempfile.mkstemp(dir=tmp_dir) + with open(tmp_file, "wb") as f: + f.write(b"*" * 1024) + return tmp_dir + + +# pytest > 2.9 only needs @pytest.fixture +@pytest.yield_fixture +def temp_dir_1024_delete(): + """ Create a temporary directory that has a single file of a + particular size (1024 bytes). The temporary directory (including + the file inside) will be deleted after fixture usage. + """ + + with tempfile.TemporaryDirectory() as tmp_dir: + tmp_file, tmp_file_path = tempfile.mkstemp(dir=tmp_dir) + with open(tmp_file, "wb") as f: + f.write(b"*" * 1024) + yield tmp_dir + + +@pytest.fixture +def temp_file_1024(): + """ Create a temporary file of a particular size (1024 bytes). """ + + with tempfile.NamedTemporaryFile(delete=False) as tmp_file: + tmp_file.write(b"*" * 1024) + return tmp_file.name + + +# pytest > 2.9 only needs @pytest.fixture +@pytest.yield_fixture +def temp_file_1024_delete(): + """ + Create a temporary file of a particular size (1024 bytes). + The temporary file will be deleted after fixture usage. + """ + + with tempfile.NamedTemporaryFile() as tmp_file: + tmp_file.write(b"*" * 1024) + tmp_file.flush() + yield tmp_file.name + + +# pytest > 2.9 only needs @pytest.fixture +@pytest.yield_fixture(scope="session") +def custom_zw(): + zw = web.share_mode.ZipWriter( + common.Common(), + zip_filename=common.Common.random_string(4, 6), + processed_size_callback=lambda _: "custom_callback", + ) + yield zw + zw.close() + os.remove(zw.zip_filename) + + +# pytest > 2.9 only needs @pytest.fixture +@pytest.yield_fixture(scope="session") +def default_zw(): + zw = web.share_mode.ZipWriter(common.Common()) + yield zw + zw.close() + tmp_dir = os.path.dirname(zw.zip_filename) + shutil.rmtree(tmp_dir) + + +@pytest.fixture +def locale_en(monkeypatch): + monkeypatch.setattr("locale.getdefaultlocale", lambda: ("en_US", "UTF-8")) + + +@pytest.fixture +def locale_fr(monkeypatch): + monkeypatch.setattr("locale.getdefaultlocale", lambda: ("fr_FR", "UTF-8")) + + +@pytest.fixture +def locale_invalid(monkeypatch): + monkeypatch.setattr("locale.getdefaultlocale", lambda: ("xx_XX", "UTF-8")) + + +@pytest.fixture +def locale_ru(monkeypatch): + monkeypatch.setattr("locale.getdefaultlocale", lambda: ("ru_RU", "UTF-8")) + + +@pytest.fixture +def platform_darwin(monkeypatch): + monkeypatch.setattr("platform.system", lambda: "Darwin") + + +@pytest.fixture # (scope="session") +def platform_linux(monkeypatch): + monkeypatch.setattr("platform.system", lambda: "Linux") + + +@pytest.fixture +def platform_windows(monkeypatch): + monkeypatch.setattr("platform.system", lambda: "Windows") + + +@pytest.fixture +def sys_argv_sys_prefix(monkeypatch): + monkeypatch.setattr("sys.argv", [sys.prefix]) + + +@pytest.fixture +def sys_frozen(monkeypatch): + monkeypatch.setattr("sys.frozen", True, raising=False) + + +@pytest.fixture +def sys_meipass(monkeypatch): + monkeypatch.setattr("sys._MEIPASS", os.path.expanduser("~"), raising=False) + + +@pytest.fixture # (scope="session") +def sys_onionshare_dev_mode(monkeypatch): + monkeypatch.setattr("sys.onionshare_dev_mode", True, raising=False) + + +@pytest.fixture +def time_time_100(monkeypatch): + monkeypatch.setattr("time.time", lambda: 100) + + +@pytest.fixture +def time_strftime(monkeypatch): + monkeypatch.setattr("time.strftime", lambda _: "Jun 06 2013 11:05:00") + + +@pytest.fixture +def common_obj(): + return common.Common() + + +@pytest.fixture +def settings_obj(sys_onionshare_dev_mode, platform_linux): + _common = common.Common() + _common.version = "DUMMY_VERSION_1.2.3" + strings.load_strings(_common) + return settings.Settings(_common) diff --git a/tests2/test_tabs.py b/tests2/test_tabs.py new file mode 100644 index 00000000..8e8bf775 --- /dev/null +++ b/tests2/test_tabs.py @@ -0,0 +1,231 @@ +#!/usr/bin/env python3 +import pytest +import unittest + +import json +import os +import requests +import shutil +import base64 +import tempfile +import secrets + +from PyQt5 import QtCore, QtTest, QtWidgets + +from onionshare import strings +from onionshare.common import Common +from onionshare.settings import Settings +from onionshare.onion import Onion +from onionshare.web import Web +from onionshare_gui import Application, MainWindow, GuiCommon + + +class TestTabs(unittest.TestCase): + @classmethod + def setUpClass(cls): + common = Common() + qtapp = Application(common) + common.gui = GuiCommon(common, qtapp, local_only=True) + cls.gui = MainWindow(common, filenames=None) + cls.gui.qtapp = qtapp + + # Create some random files to test with + cls.tmpdir = tempfile.TemporaryDirectory() + cls.tmpfiles = [] + for _ in range(10): + filename = os.path.join(cls.tmpdir.name, f"{secrets.token_hex(4)}.txt") + with open(filename, "w") as file: + file.write(secrets.token_hex(10)) + cls.tmpfiles.append(filename) + + @classmethod + def tearDownClass(cls): + cls.gui.cleanup() + + @pytest.mark.gui + def test_001_gui_loaded(self): + """Test that the GUI actually is shown""" + self.assertTrue(self.gui.show) + + @pytest.mark.gui + def test_002_window_title_seen(self): + """Test that the window title is OnionShare""" + self.assertEqual(self.gui.windowTitle(), "OnionShare") + + @pytest.mark.gui + def test_003_settings_button_is_visible(self): + """Test that the settings button is visible""" + self.assertTrue(self.gui.settings_button.isVisible()) + + @pytest.mark.gui + def test_004_server_status_bar_is_visible(self): + """Test that the status bar is visible""" + self.assertTrue(self.gui.status_bar.isVisible()) + + @pytest.mark.gui + def test_005_starts_with_one_new_tab(self): + """There should be one "New Tab" tab open""" + self.assertEqual(self.gui.tabs.count(), 1) + self.assertTrue(self.gui.tabs.widget(0).new_tab.isVisible()) + + @pytest.mark.gui + def test_006_new_tab_button_opens_new_tabs(self): + """Clicking the "+" button should open new tabs""" + self.assertEqual(self.gui.tabs.count(), 1) + QtTest.QTest.mouseClick(self.gui.tabs.new_tab_button, QtCore.Qt.LeftButton) + QtTest.QTest.mouseClick(self.gui.tabs.new_tab_button, QtCore.Qt.LeftButton) + QtTest.QTest.mouseClick(self.gui.tabs.new_tab_button, QtCore.Qt.LeftButton) + self.assertEqual(self.gui.tabs.count(), 4) + + @pytest.mark.gui + def test_007_close_tab_button_closes_tabs(self): + """Clicking the "x" button should close tabs""" + self.assertEqual(self.gui.tabs.count(), 4) + QtTest.QTest.mouseClick( + self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide), + QtCore.Qt.LeftButton, + ) + QtTest.QTest.mouseClick( + self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide), + QtCore.Qt.LeftButton, + ) + QtTest.QTest.mouseClick( + self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide), + QtCore.Qt.LeftButton, + ) + self.assertEqual(self.gui.tabs.count(), 1) + + @pytest.mark.gui + def test_008_closing_last_tab_opens_new_one(self): + """Closing the last tab should open a new tab""" + self.assertEqual(self.gui.tabs.count(), 1) + + # Click share button + QtTest.QTest.mouseClick( + self.gui.tabs.widget(0).share_button, QtCore.Qt.LeftButton + ) + self.assertFalse(self.gui.tabs.widget(0).new_tab.isVisible()) + self.assertTrue(self.gui.tabs.widget(0).share_mode.isVisible()) + + # Close the tab + QtTest.QTest.mouseClick( + self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide), + QtCore.Qt.LeftButton, + ) + + # A new tab should be opened + self.assertEqual(self.gui.tabs.count(), 1) + self.assertTrue(self.gui.tabs.widget(0).new_tab.isVisible()) + + @pytest.mark.gui + def test_009_new_tab_mode_buttons_show_correct_modes(self): + """Clicking the mode buttons in a new tab should change the mode of the tab""" + + # New tab, share files + QtTest.QTest.mouseClick(self.gui.tabs.new_tab_button, QtCore.Qt.LeftButton) + QtTest.QTest.mouseClick( + self.gui.tabs.widget(1).share_button, QtCore.Qt.LeftButton + ) + self.assertFalse(self.gui.tabs.widget(1).new_tab.isVisible()) + self.assertTrue(self.gui.tabs.widget(1).share_mode.isVisible()) + + # New tab, receive files + QtTest.QTest.mouseClick(self.gui.tabs.new_tab_button, QtCore.Qt.LeftButton) + QtTest.QTest.mouseClick( + self.gui.tabs.widget(2).receive_button, QtCore.Qt.LeftButton + ) + self.assertFalse(self.gui.tabs.widget(2).new_tab.isVisible()) + self.assertTrue(self.gui.tabs.widget(2).receive_mode.isVisible()) + + # New tab, publish website + QtTest.QTest.mouseClick(self.gui.tabs.new_tab_button, QtCore.Qt.LeftButton) + QtTest.QTest.mouseClick( + self.gui.tabs.widget(3).website_button, QtCore.Qt.LeftButton + ) + self.assertFalse(self.gui.tabs.widget(3).new_tab.isVisible()) + self.assertTrue(self.gui.tabs.widget(3).website_mode.isVisible()) + + # Close tabs + QtTest.QTest.mouseClick( + self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide), + QtCore.Qt.LeftButton, + ) + QtTest.QTest.mouseClick( + self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide), + QtCore.Qt.LeftButton, + ) + QtTest.QTest.mouseClick( + self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide), + QtCore.Qt.LeftButton, + ) + QtTest.QTest.mouseClick( + self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide), + QtCore.Qt.LeftButton, + ) + + @pytest.mark.gui + def test_010_close_share_tab_while_server_started_should_warn(self): + """Closing a share mode tab when the server is running should throw a warning""" + pass + """ + tab = self.gui.tabs.widget(0) + + # Share files + QtTest.QTest.mouseClick(tab.share_button, QtCore.Qt.LeftButton) + self.assertFalse(tab.new_tab.isVisible()) + self.assertTrue(tab.share_mode.isVisible()) + + # Add files + for filename in self.tmpfiles: + tab.share_mode.server_status.file_selection.file_list.add_file(filename) + + # Start the server + self.assertEqual( + tab.share_mode.server_status.status, + tab.share_mode.server_status.STATUS_STOPPED, + ) + QtTest.QTest.mouseClick( + tab.share_mode.server_status.server_button, QtCore.Qt.LeftButton + ) + self.assertEqual( + tab.share_mode.server_status.status, + tab.share_mode.server_status.STATUS_WORKING, + ) + QtTest.QTest.qWait(1000) + self.assertEqual( + tab.share_mode.server_status.status, + tab.share_mode.server_status.STATUS_STARTED, + ) + + # Close tab + QtTest.QTest.mouseClick( + self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide), + QtCore.Qt.LeftButton, + ) + + # The active window should now be a dialog + dialog = self.gui.qtapp.activeWindow() + self.assertEqual(type(dialog), QtWidgets.QMessageBox) + + # Reject it -- the share mode tab should still be open + dialog.reject() + self.assertFalse(tab.new_tab.isVisible()) + self.assertTrue(tab.share_mode.isVisible()) + + # Close tab + QtTest.QTest.mouseClick( + self.gui.tabs.tabBar().tabButton(0, QtWidgets.QTabBar.RightSide), + QtCore.Qt.LeftButton, + ) + + # This time accept it -- the share mode tab should be closed + dialog = self.gui.qtapp.activeWindow() + self.assertEqual(type(dialog), QtWidgets.QMessageBox) + dialog.accept() + + self.assertTrue(self.gui.tabs.widget(0).new_tab.isVisible()) + """ + + +if __name__ == "__main__": + unittest.main()