#!/usr/bin/python # # The Qubes OS Project, https://www.qubes-os.org/ # # Copyright (C) 2017 boring-stuff <boring-stuff@users.noreply.github.com> # # This library is free software; you can redistribute it and/or # modify it under the terms of the GNU Lesser General Public # License as published by the Free Software Foundation; either # version 2.1 of the License, or (at your option) any later version. # # This library is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # Lesser General Public License for more details. # # You should have received a copy of the GNU Lesser General Public # License along with this library; if not, see <https://www.gnu.org/licenses/>. # import sys import unittest from qubespolicy.tests.gtkhelpers import GtkTestCase, FocusStealingHelperMock from qubespolicy.tests.gtkhelpers import mock_domains_info, mock_whitelist from qubespolicy.gtkhelpers import VMListModeler from qubespolicy.rpcconfirmation import RPCConfirmationWindow class MockRPCConfirmationWindow(RPCConfirmationWindow): def _new_vm_list_modeler(self): return VMListModeler(mock_domains_info) def _new_focus_stealing_helper(self): return FocusStealingHelperMock( self._rpc_window, self._rpc_ok_button, self._focus_stealing_seconds) def __init__(self, source, rpc_operation, whitelist, target=None, focus_stealing_seconds=1): self._focus_stealing_seconds = focus_stealing_seconds RPCConfirmationWindow.__init__( self, mock_domains_info, source, rpc_operation, whitelist, target) def is_error_visible(self): return self._error_bar.get_visible() def get_shown_domains(self): model = self._rpc_combo_box.get_model() model_iter = model.get_iter_first() domains = [] while model_iter is not None: domain_name = model.get_value(model_iter, 1) domains += [domain_name] model_iter = model.iter_next(model_iter) return domains class RPCConfirmationWindowTestBase(MockRPCConfirmationWindow, GtkTestCase): def __init__(self, test_method, source_name="test-source", rpc_operation="test.Operation", whitelist=mock_whitelist, target_name=None): GtkTestCase.__init__(self, test_method) self.test_source_name = source_name self.test_rpc_operation = rpc_operation self.test_target_name = target_name self._test_time = 0.1 self.test_called_close = False self.test_called_show = False self.test_clicked_ok = False self.test_clicked_cancel = False MockRPCConfirmationWindow.__init__(self, self.test_source_name, self.test_rpc_operation, whitelist, self.test_target_name, focus_stealing_seconds=self._test_time) def _can_perform_action(self): return True def _close(self): self.test_called_close = True def _show(self): self.test_called_show = True def _clicked_ok(self, button): MockRPCConfirmationWindow._clicked_ok(self, button) self.test_clicked_ok = True def _clicked_cancel(self, button): MockRPCConfirmationWindow._clicked_cancel(self, button) self.test_clicked_cancel = True def test_has_linked_the_fields(self): self.assertIsNotNone(self._rpc_window) self.assertIsNotNone(self._rpc_ok_button) self.assertIsNotNone(self._rpc_cancel_button) self.assertIsNotNone(self._rpc_label) self.assertIsNotNone(self._source_entry) self.assertIsNotNone(self._rpc_combo_box) self.assertIsNotNone(self._error_bar) self.assertIsNotNone(self._error_message) def test_is_showing_source(self): self.assertTrue(self.test_source_name in self._source_entry.get_text()) def test_is_showing_operation(self): self.assertTrue(self.test_rpc_operation in self._rpc_label.get_text()) def test_escape_and_format_rpc_text(self): self.assertEquals("qubes.<b>Test</b>", self._escape_and_format_rpc_text("qubes.Test")) self.assertEquals("custom.<b>Domain</b>", self._escape_and_format_rpc_text("custom.Domain")) self.assertEquals("<b>nodomain</b>", self._escape_and_format_rpc_text("nodomain")) self.assertEquals("domain.<b>Sub.Operation</b>", self._escape_and_format_rpc_text("domain.Sub.Operation")) self.assertEquals("<b></b>", self._escape_and_format_rpc_text("")) self.assertEquals("<b>.</b>", self._escape_and_format_rpc_text(".")) self.assertEquals("inject.<b><script></b>", self._escape_and_format_rpc_text("inject.<script>")) self.assertEquals("<script>.<b>inject</b>", self._escape_and_format_rpc_text("<script>.inject")) def test_lifecycle_open_select_ok(self): self._lifecycle_start(select_target=True) self._lifecycle_click(click_type="ok") def test_lifecycle_open_select_cancel(self): self._lifecycle_start(select_target=True) self._lifecycle_click(click_type="cancel") def test_lifecycle_open_select_exit(self): self._lifecycle_start(select_target=True) self._lifecycle_click(click_type="exit") def test_lifecycle_open_cancel(self): self._lifecycle_start(select_target=False) self._lifecycle_click(click_type="cancel") def test_lifecycle_open_exit(self): self._lifecycle_start(select_target=False) self._lifecycle_click(click_type="exit") def _lifecycle_click(self, click_type): if click_type == "ok": self._rpc_ok_button.clicked() self.assertTrue(self.test_clicked_ok) self.assertFalse(self.test_clicked_cancel) self.assertTrue(self._confirmed) self.assertIsNotNone(self._target_name) elif click_type == "cancel": self._rpc_cancel_button.clicked() self.assertFalse(self.test_clicked_ok) self.assertTrue(self.test_clicked_cancel) self.assertFalse(self._confirmed) elif click_type == "exit": self._close() self.assertFalse(self.test_clicked_ok) self.assertFalse(self.test_clicked_cancel) self.assertIsNone(self._confirmed) self.assertTrue(self.test_called_close) def _lifecycle_start(self, select_target): self.assertFalse(self.test_called_close) self.assertFalse(self.test_called_show) self.assert_initial_state(False) self.assertTrue(isinstance(self._focus_helper, FocusStealingHelperMock)) # Need the following because of pylint's complaints if isinstance(self._focus_helper, FocusStealingHelperMock): FocusStealingHelperMock.simulate_focus(self._focus_helper) self.flush_gtk_events(self._test_time*2) self.assert_initial_state(True) try: # We expect the call to exit immediately, since no window is opened self.confirm_rpc() except Exception: pass self.assertFalse(self.test_called_close) self.assertTrue(self.test_called_show) self.assert_initial_state(True) if select_target: self._rpc_combo_box.set_active(1) self.assertTrue(self._rpc_ok_button.get_sensitive()) self.assertIsNotNone(self._target_name) self.assertFalse(self.test_called_close) self.assertTrue(self.test_called_show) self.assertFalse(self.test_clicked_ok) self.assertFalse(self.test_clicked_cancel) self.assertFalse(self._confirmed) def assert_initial_state(self, after_focus_timer): self.assertIsNone(self._target_name) self.assertFalse(self.test_clicked_ok) self.assertFalse(self.test_clicked_cancel) self.assertFalse(self._confirmed) self.assertFalse(self._rpc_ok_button.get_sensitive()) self.assertFalse(self._error_bar.get_visible()) if after_focus_timer: self.assertTrue(self._focus_helper.can_perform_action()) else: self.assertFalse(self._focus_helper.can_perform_action()) class RPCConfirmationWindowTestWithTarget(RPCConfirmationWindowTestBase): def __init__(self, test_method): RPCConfirmationWindowTestBase.__init__(self, test_method, source_name="test-source", rpc_operation="test.Operation", target_name="test-target") def test_lifecycle_open_ok(self): self._lifecycle_start(select_target=False) self._lifecycle_click(click_type="ok") def assert_initial_state(self, after_focus_timer): self.assertIsNotNone(self._target_name) self.assertFalse(self.test_clicked_ok) self.assertFalse(self.test_clicked_cancel) self.assertFalse(self._confirmed) if after_focus_timer: self.assertTrue(self._rpc_ok_button.get_sensitive()) self.assertTrue(self._focus_helper.can_perform_action()) else: self.assertFalse(self._rpc_ok_button.get_sensitive()) self.assertFalse(self._focus_helper.can_perform_action()) def _lifecycle_click(self, click_type): RPCConfirmationWindowTestBase._lifecycle_click(self, click_type) self.assertIsNotNone(self._target_name) class RPCConfirmationWindowTestWithTargetInvalid(unittest.TestCase): def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) def test_unknown(self): self.assert_raises_error(True, "test-source", "test-wrong-target") def test_empty(self): self.assert_raises_error(True, "test-source", "") def test_equals_source(self): self.assert_raises_error(True, "test-source", "test-source") def assert_raises_error(self, expect, source, target): rpcWindow = MockRPCConfirmationWindow(source, "test.Operation", mock_whitelist, target=target) self.assertEquals(expect, rpcWindow.is_error_visible()) class RPCConfirmationWindowTestWhitelist(unittest.TestCase): def __init__(self, *args, **kwargs): unittest.TestCase.__init__(self, *args, **kwargs) def test_no_domains(self): self._assert_whitelist([], []) def test_all_red_domains(self): self._assert_whitelist(["test-red1", "test-red2", "test-red3"], ["test-red1", "test-red2", "test-red3"]) def test_all_red_domains_plus_nonexistent(self): self._assert_whitelist( ["test-red1", "test-red2", "test-red3", "test-blue1", "test-blue2", "test-blue3"], ["test-red1", "test-red2", "test-red3"]) def test_all_allowed_domains(self): self._assert_whitelist( ["test-red1", "test-red2", "test-red3", "test-target", "$dispvm:test-disp6", "test-source", "dom0"], ["test-red1", "test-red2", "test-red3", "test-target", "Disposable VM (test-disp6)", "test-source", "dom0"]) def _assert_whitelist(self, whitelist, expected): rpcWindow = MockRPCConfirmationWindow( "test-source", "test.Operation", whitelist) domains = rpcWindow.get_shown_domains() self.assertCountEqual(domains, expected) if __name__ == '__main__': test = False window = False if len(sys.argv) == 1 or sys.argv[1] == '-t': test = True elif sys.argv[1] == '-w': window = True else: print("Usage: " + __file__ + " [-t|-w]") if window: print(MockRPCConfirmationWindow("test-source", "qubes.Filecopy", mock_whitelist, "test-red1").confirm_rpc()) elif test: unittest.main(argv=[sys.argv[0]])