From eee1fa4e801714ea761832e03aab4d4ca07c04d8 Mon Sep 17 00:00:00 2001 From: Micah Lee Date: Fri, 8 Nov 2019 16:20:38 +0800 Subject: [PATCH] Start moving over share tests --- tests2/gui_base_test.py | 36 +--- tests2/test_gui_share.py | 388 +++++++++++++++++++++++++++++++++++++++ tests2/test_gui_tabs.py | 5 + 3 files changed, 395 insertions(+), 34 deletions(-) create mode 100644 tests2/test_gui_share.py diff --git a/tests2/gui_base_test.py b/tests2/gui_base_test.py index bb44627f..66f5e19d 100644 --- a/tests2/gui_base_test.py +++ b/tests2/gui_base_test.py @@ -43,6 +43,7 @@ class GuiBaseTest(unittest.TestCase): @classmethod def tearDownClass(cls): + cls.gui.close() cls.gui.cleanup() # Shared test methods @@ -304,39 +305,6 @@ class GuiBaseTest(unittest.TestCase): # We should have timed out now self.assertEqual(mode.server_status.status, 0) - # Auto-start timer tests - def set_autostart_timer(self, mode, timer): - """Test that the timer can be set""" - schedule = QtCore.QDateTime.currentDateTime().addSecs(timer) - mode.server_status.autostart_timer_widget.setDateTime(schedule) - self.assertTrue(mode.server_status.autostart_timer_widget.dateTime(), schedule) - - def autostart_timer_widget_hidden(self, mode): - """Test that the auto-start timer widget is hidden when share has started""" - self.assertFalse(mode.server_status.autostart_timer_container.isVisible()) - - def scheduled_service_started(self, mode, wait): - """Test that the server has timed out after the timer ran out""" - QtTest.QTest.qWait(wait) - # We should have started now - self.assertEqual(mode.server_status.status, 2) - - def cancel_the_share(self, mode): - """Test that we can cancel a share before it's started up """ - self.server_working_on_start_button_pressed(mode) - self.server_status_indicator_says_scheduled(mode) - self.add_delete_buttons_hidden() - self.settings_button_is_hidden() - self.set_autostart_timer(mode, 10) - QtTest.QTest.mousePress(mode.server_status.server_button, QtCore.Qt.LeftButton) - QtTest.QTest.qWait(2000) - QtTest.QTest.mouseRelease( - mode.server_status.server_button, QtCore.Qt.LeftButton - ) - self.assertEqual(mode.server_status.status, 0) - self.server_is_stopped(mode, False) - self.web_server_is_stopped() - # Hack to close an Alert dialog that would otherwise block tests def accept_dialog(self): window = self.gui.qtapp.activeWindow() @@ -344,7 +312,7 @@ class GuiBaseTest(unittest.TestCase): window.close() # Grouped tests follow from here - + def run_all_common_setup_tests(self): self.gui_loaded() self.window_title_seen() diff --git a/tests2/test_gui_share.py b/tests2/test_gui_share.py new file mode 100644 index 00000000..a6bd9e3b --- /dev/null +++ b/tests2/test_gui_share.py @@ -0,0 +1,388 @@ +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 + +from .gui_base_test import GuiBaseTest + + +class TestShare(GuiBaseTest): + # Shared test methods + + # Persistence tests + def have_same_password(self, password): + """Test that we have the same password""" + self.assertEqual(self.gui.share_mode.server_status.web.password, password) + + # Share-specific tests + + def file_selection_widget_has_files(self, num=2): + """Test that the number of items in the list is as expected""" + self.assertEqual( + self.gui.share_mode.server_status.file_selection.get_num_files(), num + ) + + def deleting_all_files_hides_delete_button(self): + """Test that clicking on the file item shows the delete button. Test that deleting the only item in the list hides the delete button""" + rect = self.gui.share_mode.server_status.file_selection.file_list.visualItemRect( + self.gui.share_mode.server_status.file_selection.file_list.item(0) + ) + QtTest.QTest.mouseClick( + self.gui.share_mode.server_status.file_selection.file_list.viewport(), + QtCore.Qt.LeftButton, + pos=rect.center(), + ) + # Delete button should be visible + self.assertTrue( + self.gui.share_mode.server_status.file_selection.delete_button.isVisible() + ) + # Click delete, delete button should still be visible since we have one more file + QtTest.QTest.mouseClick( + self.gui.share_mode.server_status.file_selection.delete_button, + QtCore.Qt.LeftButton, + ) + + rect = self.gui.share_mode.server_status.file_selection.file_list.visualItemRect( + self.gui.share_mode.server_status.file_selection.file_list.item(0) + ) + QtTest.QTest.mouseClick( + self.gui.share_mode.server_status.file_selection.file_list.viewport(), + QtCore.Qt.LeftButton, + pos=rect.center(), + ) + self.assertTrue( + self.gui.share_mode.server_status.file_selection.delete_button.isVisible() + ) + QtTest.QTest.mouseClick( + self.gui.share_mode.server_status.file_selection.delete_button, + QtCore.Qt.LeftButton, + ) + + # No more files, the delete button should be hidden + self.assertFalse( + self.gui.share_mode.server_status.file_selection.delete_button.isVisible() + ) + + def add_a_file_and_delete_using_its_delete_widget(self): + """Test that we can also delete a file by clicking on its [X] widget""" + self.gui.share_mode.server_status.file_selection.file_list.add_file( + "/etc/hosts" + ) + QtTest.QTest.mouseClick( + self.gui.share_mode.server_status.file_selection.file_list.item( + 0 + ).item_button, + QtCore.Qt.LeftButton, + ) + self.file_selection_widget_has_files(0) + + def file_selection_widget_read_files(self): + """Re-add some files to the list so we can share""" + self.gui.share_mode.server_status.file_selection.file_list.add_file( + "/etc/hosts" + ) + self.gui.share_mode.server_status.file_selection.file_list.add_file( + "/tmp/test.txt" + ) + self.file_selection_widget_has_files(2) + + def add_large_file(self): + """Add a large file to the share""" + size = 1024 * 1024 * 155 + with open("/tmp/large_file", "wb") as fout: + fout.write(os.urandom(size)) + self.gui.share_mode.server_status.file_selection.file_list.add_file( + "/tmp/large_file" + ) + + def add_delete_buttons_hidden(self): + """Test that the add and delete buttons are hidden when the server starts""" + self.assertFalse( + self.gui.share_mode.server_status.file_selection.add_button.isVisible() + ) + self.assertFalse( + self.gui.share_mode.server_status.file_selection.delete_button.isVisible() + ) + + def download_share(self, public_mode): + """Test that we can download the share""" + url = f"http://127.0.0.1:{self.gui.app.port}/download" + if public_mode: + r = requests.get(url) + else: + r = requests.get( + url, + auth=requests.auth.HTTPBasicAuth( + "onionshare", self.gui.share_mode.server_status.web.password + ), + ) + + tmp_file = tempfile.NamedTemporaryFile() + with open(tmp_file.name, "wb") as f: + f.write(r.content) + + zip = zipfile.ZipFile(tmp_file.name) + QtTest.QTest.qWait(2000) + self.assertEqual("onionshare", zip.read("test.txt").decode("utf-8")) + + def individual_file_is_viewable_or_not(self, public_mode, stay_open): + """Test whether an individual file is viewable (when in stay_open mode) and that it isn't (when not in stay_open mode)""" + url = f"http://127.0.0.1:{self.gui.app.port}" + download_file_url = f"http://127.0.0.1:{self.gui.app.port}/test.txt" + if public_mode: + r = requests.get(url) + else: + r = requests.get( + url, + auth=requests.auth.HTTPBasicAuth( + "onionshare", self.gui.share_mode.server_status.web.password + ), + ) + + if stay_open: + self.assertTrue('a href="test.txt"' in r.text) + + if public_mode: + r = requests.get(download_file_url) + else: + r = requests.get( + download_file_url, + auth=requests.auth.HTTPBasicAuth( + "onionshare", self.gui.share_mode.server_status.web.password + ), + ) + + tmp_file = tempfile.NamedTemporaryFile() + with open(tmp_file.name, "wb") as f: + f.write(r.content) + + with open(tmp_file.name, "r") as f: + self.assertEqual("onionshare", f.read()) + else: + self.assertFalse('a href="/test.txt"' in r.text) + if public_mode: + r = requests.get(download_file_url) + else: + r = requests.get( + download_file_url, + auth=requests.auth.HTTPBasicAuth( + "onionshare", self.gui.share_mode.server_status.web.password + ), + ) + self.assertEqual(r.status_code, 404) + self.download_share(public_mode) + + QtTest.QTest.qWait(2000) + + def hit_401(self, public_mode): + """Test that the server stops after too many 401s, or doesn't when in public_mode""" + url = f"http://127.0.0.1:{self.gui.app.port}/" + + for _ in range(20): + password_guess = self.gui.common.build_password() + r = requests.get( + url, auth=requests.auth.HTTPBasicAuth("onionshare", password_guess) + ) + + # A nasty hack to avoid the Alert dialog that blocks the rest of the test + if not public_mode: + QtCore.QTimer.singleShot(1000, self.accept_dialog) + + # In public mode, we should still be running (no rate-limiting) + if public_mode: + self.web_server_is_running() + # In non-public mode, we should be shut down (rate-limiting) + else: + self.web_server_is_stopped() + + # Grouped tests follow from here + + def run_all_share_mode_setup_tests(self): + """Tests in share mode prior to starting a share""" + self.click_mode(self.gui.share_mode) + self.file_selection_widget_has_files() + self.history_is_not_visible(self.gui.share_mode) + self.click_toggle_history(self.gui.share_mode) + self.history_is_visible(self.gui.share_mode) + self.deleting_all_files_hides_delete_button() + self.add_a_file_and_delete_using_its_delete_widget() + self.file_selection_widget_read_files() + + def run_all_share_mode_started_tests(self, public_mode, startup_time=2000): + """Tests in share mode after starting a share""" + self.server_working_on_start_button_pressed(self.gui.share_mode) + self.server_status_indicator_says_starting(self.gui.share_mode) + self.add_delete_buttons_hidden() + self.settings_button_is_hidden() + self.server_is_started(self.gui.share_mode, startup_time) + self.web_server_is_running() + self.have_a_password(self.gui.share_mode, public_mode) + self.url_description_shown(self.gui.share_mode) + self.have_copy_url_button(self.gui.share_mode, public_mode) + self.server_status_indicator_says_started(self.gui.share_mode) + + def run_all_share_mode_download_tests(self, public_mode, stay_open): + """Tests in share mode after downloading a share""" + self.web_page(self.gui.share_mode, "Total size", public_mode) + self.download_share(public_mode) + self.history_widgets_present(self.gui.share_mode) + self.server_is_stopped(self.gui.share_mode, stay_open) + self.web_server_is_stopped() + self.server_status_indicator_says_closed(self.gui.share_mode, stay_open) + self.add_button_visible(self.gui.share_mode) + self.server_working_on_start_button_pressed(self.gui.share_mode) + self.toggle_indicator_is_reset(self.gui.share_mode) + self.server_is_started(self.gui.share_mode) + self.history_indicator(self.gui.share_mode, public_mode) + + def run_all_share_mode_individual_file_download_tests(self, public_mode, stay_open): + """Tests in share mode after downloading a share""" + self.web_page(self.gui.share_mode, "Total size", public_mode) + self.individual_file_is_viewable_or_not(public_mode, stay_open) + self.history_widgets_present(self.gui.share_mode) + self.server_is_stopped(self.gui.share_mode, stay_open) + self.web_server_is_stopped() + self.server_status_indicator_says_closed(self.gui.share_mode, stay_open) + self.add_button_visible(self.gui.share_mode) + self.server_working_on_start_button_pressed(self.gui.share_mode) + self.server_is_started(self.gui.share_mode) + self.history_indicator(self.gui.share_mode, public_mode) + + def run_all_share_mode_tests(self, public_mode, stay_open): + """End-to-end share tests""" + self.run_all_share_mode_setup_tests() + self.run_all_share_mode_started_tests(public_mode) + self.run_all_share_mode_download_tests(public_mode, stay_open) + + def run_all_clear_all_button_tests(self, public_mode, stay_open): + """Test the Clear All history button""" + self.run_all_share_mode_setup_tests() + self.run_all_share_mode_started_tests(public_mode) + self.individual_file_is_viewable_or_not(public_mode, stay_open) + self.history_widgets_present(self.gui.share_mode) + self.clear_all_history_items(self.gui.share_mode, 0) + self.individual_file_is_viewable_or_not(public_mode, stay_open) + self.clear_all_history_items(self.gui.share_mode, 2) + + def run_all_share_mode_individual_file_tests(self, public_mode, stay_open): + """Tests in share mode when viewing an individual file""" + self.run_all_share_mode_setup_tests() + self.run_all_share_mode_started_tests(public_mode) + self.run_all_share_mode_individual_file_download_tests(public_mode, stay_open) + + def run_all_large_file_tests(self, public_mode, stay_open): + """Same as above but with a larger file""" + self.run_all_share_mode_setup_tests() + self.add_large_file() + self.run_all_share_mode_started_tests(public_mode, startup_time=15000) + self.assertTrue(self.gui.share_mode.filesize_warning.isVisible()) + self.server_is_stopped(self.gui.share_mode, stay_open) + self.web_server_is_stopped() + self.server_status_indicator_says_closed(self.gui.share_mode, stay_open) + + def run_all_share_mode_persistent_tests(self, public_mode, stay_open): + """Same as end-to-end share tests but also test the password is the same on multiple shared""" + self.run_all_share_mode_setup_tests() + self.run_all_share_mode_started_tests(public_mode) + password = self.gui.share_mode.server_status.web.password + self.run_all_share_mode_download_tests(public_mode, stay_open) + self.have_same_password(password) + + def run_all_share_mode_timer_tests(self, public_mode): + """Auto-stop timer tests in share mode""" + self.run_all_share_mode_setup_tests() + self.set_timeout(self.gui.share_mode, 5) + self.run_all_share_mode_started_tests(public_mode) + self.autostop_timer_widget_hidden(self.gui.share_mode) + self.server_timed_out(self.gui.share_mode, 10000) + self.web_server_is_stopped() + + def run_all_share_mode_autostart_timer_tests(self, public_mode): + """Auto-start timer tests in share mode""" + self.run_all_share_mode_setup_tests() + self.set_autostart_timer(self.gui.share_mode, 5) + self.server_working_on_start_button_pressed(self.gui.share_mode) + self.autostart_timer_widget_hidden(self.gui.share_mode) + self.server_status_indicator_says_scheduled(self.gui.share_mode) + self.web_server_is_stopped() + self.scheduled_service_started(self.gui.share_mode, 7000) + self.web_server_is_running() + + def run_all_share_mode_autostop_autostart_mismatch_tests(self, public_mode): + """Auto-stop timer tests in share mode""" + self.run_all_share_mode_setup_tests() + self.set_autostart_timer(self.gui.share_mode, 15) + self.set_timeout(self.gui.share_mode, 5) + QtCore.QTimer.singleShot(4000, self.accept_dialog) + QtTest.QTest.mouseClick( + self.gui.share_mode.server_status.server_button, QtCore.Qt.LeftButton + ) + self.server_is_stopped(self.gui.share_mode, False) + + def run_all_share_mode_unreadable_file_tests(self): + """Attempt to share an unreadable file""" + self.run_all_share_mode_setup_tests() + QtCore.QTimer.singleShot(1000, self.accept_dialog) + self.gui.share_mode.server_status.file_selection.file_list.add_file( + "/tmp/nonexistent.txt" + ) + self.file_selection_widget_has_files(2) + + # Auto-start timer tests + def set_autostart_timer(self, mode, timer): + """Test that the timer can be set""" + schedule = QtCore.QDateTime.currentDateTime().addSecs(timer) + mode.server_status.autostart_timer_widget.setDateTime(schedule) + self.assertTrue(mode.server_status.autostart_timer_widget.dateTime(), schedule) + + def autostart_timer_widget_hidden(self, mode): + """Test that the auto-start timer widget is hidden when share has started""" + self.assertFalse(mode.server_status.autostart_timer_container.isVisible()) + + def scheduled_service_started(self, mode, wait): + """Test that the server has timed out after the timer ran out""" + QtTest.QTest.qWait(wait) + # We should have started now + self.assertEqual(mode.server_status.status, 2) + + def cancel_the_share(self, mode): + """Test that we can cancel a share before it's started up """ + self.server_working_on_start_button_pressed(mode) + self.server_status_indicator_says_scheduled(mode) + self.add_delete_buttons_hidden() + self.settings_button_is_hidden() + self.set_autostart_timer(mode, 10) + QtTest.QTest.mousePress(mode.server_status.server_button, QtCore.Qt.LeftButton) + QtTest.QTest.qWait(2000) + QtTest.QTest.mouseRelease( + mode.server_status.server_button, QtCore.Qt.LeftButton + ) + self.assertEqual(mode.server_status.status, 0) + self.server_is_stopped(mode, False) + self.web_server_is_stopped() + + # Tests + + @pytest.mark.gui + def test_common_tests(self): + """Run all common tests""" + self.run_all_common_setup_tests() + + +if __name__ == "__main__": + unittest.main() diff --git a/tests2/test_gui_tabs.py b/tests2/test_gui_tabs.py index 7b03d0fc..0fd3feab 100644 --- a/tests2/test_gui_tabs.py +++ b/tests2/test_gui_tabs.py @@ -334,6 +334,11 @@ class TestTabs(GuiBaseTest): # The window should still be open self.assertTrue(self.gui.isVisible()) + # Stop the server + QtTest.QTest.mouseClick( + tab.get_mode().server_status.server_button, QtCore.Qt.LeftButton + ) + if __name__ == "__main__": unittest.main()