File indexing completed on 2024-05-19 05:37:13

0001 #!/usr/bin/env python3
0002 
0003 # SPDX-FileCopyrightText: 2023 Harald Sitter <sitter@kde.org>
0004 # SPDX-FileCopyrightText: 2023 Fushan Wen <qydwhotmail@gmail.com>
0005 # SPDX-License-Identifier: GPL-2.0-or-later
0006 
0007 import subprocess
0008 import sys
0009 import time
0010 import unittest
0011 from typing import Final
0012 
0013 import gi
0014 from appium import webdriver
0015 from appium.options.common.base import AppiumOptions
0016 from appium.webdriver.common.appiumby import AppiumBy
0017 from selenium.webdriver.support import expected_conditions as EC
0018 from selenium.webdriver.support.ui import WebDriverWait
0019 from appium.webdriver.webdriver import ExtensionBase
0020 from appium.webdriver.webelement import WebElement
0021 
0022 gi.require_version('Gdk', '3.0')
0023 from gi.repository import Gdk
0024 
0025 WIDGET_ID: Final = "org.kde.plasma.volume"
0026 
0027 class SetValueCommand(ExtensionBase):
0028 
0029     def method_name(self):
0030         return "set_value"
0031 
0032     def set_value(self, element: WebElement, value: str):
0033         """
0034         Set the value on this element in the application
0035         Args:
0036             value: The value to be set
0037         """
0038         data = {
0039             "id": element.id,
0040             "text": value,
0041         }
0042         return self.execute(data)["value"]
0043 
0044     def add_command(self):
0045         return "post", "/session/$sessionId/appium/element/$id/value"
0046 
0047 
0048 class AppletTest(unittest.TestCase):
0049     """
0050     Tests for the widget
0051     """
0052 
0053     driver: webdriver.Remote | None = None
0054     pipewire: subprocess.Popen | None = None
0055     pulseaudio: subprocess.Popen | None = None
0056 
0057     @classmethod
0058     def setUpClass(cls) -> None:
0059         """
0060         Initializes the webdriver
0061         """
0062         options = AppiumOptions()
0063         options.set_capability("app", f"plasmawindowed -p org.kde.plasma.nano {WIDGET_ID}")
0064         options.set_capability("environ", {
0065             "QT_FATAL_WARNINGS": "1",
0066             "QT_LOGGING_RULES": "qt.accessibility.atspi.warning=false;kf.plasma.core.warning=false;kf.windowsystem.warning=false;kf.kirigami.platform.warning=false",
0067         })
0068         options.set_capability("timeouts", {'implicit': 30000})
0069         cls.driver = webdriver.Remote(command_executor='http://127.0.0.1:4723', extensions=[SetValueCommand], options=options)
0070 
0071     def tearDown(self) -> None:
0072         """
0073         Take screenshot when the current test fails
0074         """
0075         if not self._outcome.result.wasSuccessful():
0076             self.driver.get_screenshot_as_file(f"failed_test_shot_volume_#{self.id()}.png")
0077 
0078     @classmethod
0079     def tearDownClass(cls) -> None:
0080         """
0081         Make sure to terminate the driver again, lest it dangles.
0082         """
0083         cls.driver.quit()
0084         if cls.pulseaudio is not None:
0085             cls.pulseaudio.terminate()
0086         if cls.pipewire is not None:
0087             cls.pipewire.terminate()
0088 
0089     def test_0_open(self) -> None:
0090         """
0091         Tests the widget can be opened
0092         """
0093         self.driver.find_element(AppiumBy.NAME, "No output or input devices found")
0094 
0095     def test_1_sink_1_list_device(self) -> None:
0096         """
0097         Add a null sink and adjust its volume
0098         """
0099         self.pipewire = subprocess.Popen(["pipewire"], stdout=sys.stderr, stderr=sys.stderr)
0100         time.sleep(1)
0101         self.pulseaudio = subprocess.Popen(["pipewire-pulse"], stdout=sys.stderr, stderr=sys.stderr)
0102         time.sleep(1)
0103         description: Final = "Virtual_Dummy_Output"
0104         subprocess.check_call(["pactl", "load-module", "module-null-sink", "sink_name=DummyOutput", f"sink_properties=node.nick={description}"])
0105         self.driver.find_element(AppiumBy.NAME, description)
0106 
0107         # Raise the maximum volume
0108         self.driver.find_element(AppiumBy.NAME, "Raise maximum volume").click()
0109         slider_element: WebElement = self.driver.find_element(AppiumBy.NAME, f"Adjust volume for {description}")
0110         self.driver.set_value(slider_element, str(int(0x10000 * 1.5)))  # PA_VOLUME_NORM * 1.5
0111         slider_element.click()
0112         wait = WebDriverWait(self.driver, 30)
0113         wait.until(EC.presence_of_element_located((AppiumBy.NAME, "150%")))
0114         time.sleep(1)
0115 
0116         self.assertIn("150%", subprocess.check_output(["pactl", "list", "sinks"]).decode(encoding="utf-8"))
0117 
0118 
0119 if __name__ == '__main__':
0120     unittest.main()