anaconda/anaconda-40.22.3.13/tests/unit_tests/pyanaconda_tests/ui/test_webui.py
2024-11-14 21:39:56 -08:00

352 lines
14 KiB
Python

#
# Copyright (C) 2023 Red Hat, Inc.
#
# This copyrighted material is made available to anyone wishing to use,
# modify, copy, or redistribute it subject to the terms and conditions of
# the GNU General Public License v.2, or (at your option) any later version.
# This program is distributed in the hope that it will be useful, but WITHOUT
# ANY WARRANTY expressed or implied, including the implied warranties of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details. You should have received a copy of the
# GNU General Public License along with this program; if not, write to the
# Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA. Any Red Hat trademarks that are incorporated in the
# source code or documentation are not subject to the GNU General Public
# License and may only be used or replicated with the express permission of
# Red Hat, Inc.
#
import unittest
import pytest
import tempfile
import os
from meh.ui.text import TextIntf
from pyanaconda.ui.webui import CockpitUserInterface, FIREFOX_THEME_DEFAULT
from unittest.mock import Mock, patch
from pyanaconda.core.constants import PAYLOAD_TYPE_DNF, PAYLOAD_TYPE_LIVE_IMAGE
class SimpleWebUITestCase(unittest.TestCase):
"""Simple test case for Web UI.
The goal of this test is to test execution of the UI behaves as desired.
"""
def setUp(self):
self.intf = CockpitUserInterface(None, None, 0)
def _prepare_for_live_testing(self,
pid_file,
backend_file,
pid_content="",
remote=0):
# prepare UI interface class
self.intf = CockpitUserInterface(None, None, remote)
self.intf._backend_ready_flag_file = backend_file
open(backend_file, "wt").close()
# Value could be None
if pid_file:
self.intf._viewer_pid_file = pid_file
# wrote pid if requested
if pid_content:
with open(pid_file, "wt") as f:
f.write(pid_content)
def test_webui_defaults(self):
"""Test that webui interface has correct defaults."""
assert isinstance(self.intf.meh_interface, TextIntf)
assert self.intf.tty_num == 6
# Not implemented
assert self.intf.showYesNoQuestion("Implemented by browser") is False
@patch("pyanaconda.ui.webui.CockpitUserInterface._print_message")
def test_error_propagation(self, mocked_print_message):
"""Test that webui prints erorr to the console.
This gets then propagated by service to journal.
"""
self.intf.showError("My Error")
mocked_print_message.assert_called_once_with("My Error")
mocked_print_message.reset_mock()
self.intf.showDetailedError("My detailed error", "Such a detail!")
mocked_print_message.assert_called_once_with("My detailed error\n\nSuch a detail!""")
@patch("pyanaconda.ui.webui.flags")
def test_setup_automated_installation(self, mocked_flags):
"""Test webui setup call for automated installation."""
mocked_flags.automatedInstall = True
mocked_flags.ksprompt = True
with pytest.raises(RuntimeError) as cm:
self._setup_interface()
assert str(cm.value) == "Automated installations are not supported by Web UI."
@patch("pyanaconda.ui.webui.flags")
def test_setup_non_interactive_installation(self, mocked_flags):
"""Test webui setup call for automated installation."""
mocked_flags.automatedInstall = True
mocked_flags.ksprompt = False
with pytest.raises(RuntimeError) as cm:
self._setup_interface()
assert str(cm.value) == "Non-interactive installations are not supported by Web UI."
@patch("pyanaconda.ui.webui.conf")
def test_setup_dir_installation(self, mocked_conf):
"""Test webui setup call for a dir installation."""
mocked_conf.target.is_directory = True
mocked_conf.target.is_image = False
with pytest.raises(RuntimeError) as cm:
self._setup_interface()
assert str(cm.value) == "Dir and image installations are not supported by Web UI."
@patch("pyanaconda.ui.webui.conf")
def test_setup_image_installation(self, mocked_conf):
"""Test webui setup call for an image installation."""
mocked_conf.target.is_directory = False
mocked_conf.target.is_image = True
with pytest.raises(RuntimeError) as cm:
self._setup_interface()
assert str(cm.value) == "Dir and image installations are not supported by Web UI."
@patch("pyanaconda.ui.webui.conf")
def test_setup_unsupported_system(self, mocked_conf):
"""Test webui setup call with unsupported system."""
mocked_conf.target.is_directory = False
mocked_conf.target.is_image = False
mocked_conf.system.supports_web_ui = False
with pytest.raises(RuntimeError) as cm:
self._setup_interface()
assert str(cm.value) == "This installation environment is not supported by Web UI."
@patch("pyanaconda.ui.webui.conf")
def test_setup_unsupported_payload(self, mocked_conf):
"""Test webui setup call with unsupported payload."""
mocked_conf.target.is_directory = False
mocked_conf.target.is_image = False
mocked_conf.system.supports_web_ui = True
with pytest.raises(NotImplementedError) as cm:
self._setup_interface(payload_type=PAYLOAD_TYPE_DNF)
assert str(cm.value) == "Package installations are not supported by Web UI."
@patch("pyanaconda.ui.webui.conf")
def test_setup(self, mocked_conf):
"""Test webui setup call."""
mocked_conf.target.is_directory = False
mocked_conf.target.is_image = False
mocked_conf.system.supports_web_ui = True
self._setup_interface()
@staticmethod
def _setup_interface(payload_type=PAYLOAD_TYPE_LIVE_IMAGE):
"""Create and set up the Web UI."""
intf = CockpitUserInterface(
payload=Mock(type=payload_type),
storage=None,
remote=False,
)
intf.setup(
data=None
)
def test_backend_ready_file(self):
"""Test webui correctly create and remove beackend ready flag file."""
with tempfile.TemporaryDirectory() as fd:
backend_file = os.path.join(fd, "backend_ready")
self._prepare_for_live_testing(None, backend_file, remote=1)
def test_flag_file():
assert os.path.exists(backend_file) is True
self.intf._run_webui = test_flag_file
self.intf.run()
assert os.path.exists(backend_file) is False
@patch("pyanaconda.ui.webui.startProgram")
@patch("pyanaconda.ui.webui.conf")
def test_run_not_on_live(self, mocked_conf, mocked_startProgram):
"""Test webui run call on boot iso."""
# Execution is different for boot.iso then live environment
mocked_conf.system.provides_liveuser = False
mocked_process = Mock()
mocked_process.pid = 12345
mocked_startProgram.return_value = mocked_process
with tempfile.TemporaryDirectory() as fd:
pid_file = os.path.join(fd, "anaconda.pid")
backend_file = os.path.join(fd, "backend_ready")
self._prepare_for_live_testing(pid_file, backend_file, remote=1)
self.intf.run()
mocked_startProgram.assert_called_once_with(["/usr/libexec/webui-desktop",
"-t", FIREFOX_THEME_DEFAULT, "-r", "1",
"/cockpit/@localhost/anaconda-webui/index.html"],
reset_lang=False)
# check if backend flag file was removed after finish of run method
assert os.path.exists(backend_file) is False
with open(pid_file, "rt") as f:
assert f.readlines() == ["12345"]
mocked_process.wait.assert_called_once()
# test with disabled remote
mocked_startProgram.reset_mock()
mocked_process.reset_mock()
mocked_startProgram.return_value = mocked_process
with tempfile.TemporaryDirectory() as fd:
pid_file = os.path.join(fd, "anaconda.pid")
backend_file = os.path.join(fd, "backend_ready")
self._prepare_for_live_testing(pid_file, backend_file)
self.intf.run()
mocked_startProgram.assert_called_once_with(["/usr/libexec/webui-desktop",
"-t", FIREFOX_THEME_DEFAULT, "-r", "0",
"/cockpit/@localhost/anaconda-webui/index.html"],
reset_lang=False)
# check if backend flag file was removed after finish of run method
assert os.path.exists(backend_file) is False
with open(pid_file, "rt") as f:
assert f.readlines() == ["12345"]
mocked_process.wait.assert_called_once()
@patch("pyanaconda.ui.webui.PidWatcher.watch_process")
@patch("pyanaconda.ui.webui.create_main_loop")
@patch("pyanaconda.ui.webui.conf")
def test_run_on_live_success(self, mocked_conf, mocked_create_main_loop, mocked_watch_process):
"""Test webui run call on live environment."""
# Execution is different for live because we need to start FF early as possible
mocked_conf.system.provides_liveuser = True
mocked_main_loop = Mock()
# Test on Live media
mocked_watch_process.reset_mock()
mocked_create_main_loop.reset_mock()
mocked_create_main_loop.return_value = mocked_main_loop
mocked_main_loop.reset_mock()
with tempfile.TemporaryDirectory() as fd:
pid_file = os.path.join(fd, "anaconda.pid")
backend_file = os.path.join(fd, "backend_ready")
self._prepare_for_live_testing(pid_file, backend_file, pid_content="11111")
self.intf.run()
# check if backend flag file was removed after finish of run method
assert os.path.exists(backend_file) is False
# check that callback is correctly set
mocked_watch_process.assert_called_once_with(11111, self.intf._webui_desktop_closed)
mocked_create_main_loop.assert_called_once()
mocked_main_loop.run.assert_called_once()
# test quit callbacks by calling them (simple tests avoiding execution of the main loop)
# test webui callback execution - simulates closing the viewer app
self.intf._webui_desktop_closed(11111, 0)
mocked_main_loop.quit.assert_called_once()
# test webui callback bad status - simulates crash of the viewer app
mocked_main_loop.reset_mock()
self.intf._webui_desktop_closed(11111, 1)
mocked_main_loop.quit.assert_called_once()
@patch("pyanaconda.ui.webui.PidWatcher.watch_process")
@patch("pyanaconda.ui.webui.create_main_loop")
@patch("pyanaconda.ui.webui.conf")
def test_run_on_live_failure(self, mocked_conf, mocked_create_main_loop, mocked_watch_process):
"""Test webui run call on live environment."""
mocked_conf.system.provides_liveuser = True
mocked_main_loop = Mock()
# Test pid file doesn't exists
mocked_watch_process.reset_mock()
mocked_create_main_loop.reset_mock()
mocked_create_main_loop.return_value = mocked_main_loop
mocked_main_loop.reset_mock()
with tempfile.TemporaryDirectory() as fd:
pid_file = os.path.join(fd, "anaconda.pid")
backend_file = os.path.join(fd, "backend_ready")
self._prepare_for_live_testing(pid_file, backend_file)
with pytest.raises(FileNotFoundError):
self.intf.run()
# check if backend flag file was removed after finish of run method
assert os.path.exists(backend_file) is False
mocked_watch_process.assert_not_called()
mocked_create_main_loop.assert_not_called()
# Test empty pid file
mocked_watch_process.reset_mock()
mocked_create_main_loop.reset_mock()
mocked_create_main_loop.return_value = mocked_main_loop
mocked_main_loop.reset_mock()
with tempfile.TemporaryDirectory() as fd:
pid_file = os.path.join(fd, "anaconda.pid")
backend_file = os.path.join(fd, "backend_ready")
open(pid_file, "wt").close()
self._prepare_for_live_testing(pid_file, backend_file)
with pytest.raises(ValueError):
self.intf.run()
# check if backend flag file was removed after finish of run method
assert os.path.exists(backend_file) is False
mocked_watch_process.assert_not_called()
mocked_create_main_loop.assert_not_called()
# Test negative pid
mocked_watch_process.reset_mock()
mocked_create_main_loop.reset_mock()
mocked_create_main_loop.return_value = mocked_main_loop
mocked_main_loop.reset_mock()
with tempfile.TemporaryDirectory() as fd:
pid_file = os.path.join(fd, "anaconda.pid")
backend_file = os.path.join(fd, "backend_ready")
self._prepare_for_live_testing(pid_file, backend_file, pid_content="-20")
with pytest.raises(ValueError):
self.intf.run()
# check if backend flag file was removed after finish of run method
assert os.path.exists(backend_file) is False
mocked_watch_process.assert_not_called()
mocked_create_main_loop.assert_not_called()
# Test bad value pid
mocked_watch_process.reset_mock()
mocked_create_main_loop.reset_mock()
mocked_create_main_loop.return_value = mocked_main_loop
mocked_main_loop.reset_mock()
with tempfile.TemporaryDirectory() as fd:
pid_file = os.path.join(fd, "anaconda.pid")
backend_file = os.path.join(fd, "backend_ready")
self._prepare_for_live_testing(pid_file, backend_file, pid_content="not-a-number")
with pytest.raises(ValueError):
self.intf.run()
# check if backend flag file was removed after finish of run method
assert os.path.exists(backend_file) is False
mocked_watch_process.assert_not_called()
mocked_create_main_loop.assert_not_called()