Skip to content

Commit 7cb0145

Browse files
committed
test: type storagelib partially
Storagelib is shared between projects so useful to have typed. Typing this mixin is a bit tricky as the documented approach is to use a Protocol class which provides the required "borrowed" functions/properties. Sadly this requires a bit of unavoidable duplication of typing.
1 parent 4e028a3 commit 7cb0145

File tree

2 files changed

+44
-34
lines changed

2 files changed

+44
-34
lines changed

test/common/storagelib.py

+33-33
Original file line numberDiff line numberDiff line change
@@ -20,17 +20,17 @@
2020
import re
2121
import textwrap
2222

23-
from testlib import Error, MachineCase, wait
23+
from testlib import Error, MachineCase, MachineProtocol, wait
2424

2525

26-
def from_udisks_ascii(codepoints):
26+
def from_udisks_ascii(codepoints: list[int]) -> str:
2727
return ''.join(map(chr, codepoints[:-1]))
2828

2929

30-
class StorageHelpers:
30+
class StorageHelpers(MachineProtocol):
3131
"""Mix-in class for using in tests that derive from something else than MachineCase or StorageCase"""
3232

33-
def inode(self, f):
33+
def inode(self, f: str) -> str:
3434
return self.machine.execute("stat -L '%s' -c %%i" % f)
3535

3636
def retry(self, setup, check, teardown):
@@ -45,7 +45,7 @@ def step():
4545

4646
self.browser.wait(step)
4747

48-
def add_ram_disk(self, size=50, delay=None):
48+
def add_ram_disk(self, size: int = 50, delay: int | None = None):
4949
"""Add per-test RAM disk
5050
5151
The disk gets removed automatically when the test ends. This is safe for @nondestructive tests.
@@ -119,7 +119,7 @@ def add_targetd_loopback_disk(self, index, size=50):
119119
raise Error("Device not found")
120120
return dev
121121

122-
def force_remove_disk(self, device):
122+
def force_remove_disk(self, device: str) -> None:
123123
"""Act like the given device gets physically removed.
124124
125125
This circumvents all the normal EBUSY failures, and thus can be used for testing
@@ -140,7 +140,7 @@ def addCleanupMount(self, mount_point):
140140

141141
# Dialogs
142142

143-
def dialog_wait_open(self):
143+
def dialog_wait_open(self) -> None:
144144
self.browser.wait_visible('#dialog')
145145

146146
def dialog_wait_alert(self, text1, text2=None):
@@ -149,10 +149,10 @@ def has_alert_title():
149149
return text1 in t or (text2 is not None and text2 in t)
150150
self.browser.wait(has_alert_title)
151151

152-
def dialog_wait_title(self, text):
152+
def dialog_wait_title(self, text: str) -> None:
153153
self.browser.wait_in_text('#dialog .pf-v5-c-modal-box__title', text)
154154

155-
def dialog_field(self, field):
155+
def dialog_field(self, field: str) -> str:
156156
return f'#dialog [data-field="{field}"]'
157157

158158
def dialog_val(self, field):
@@ -233,22 +233,22 @@ def dialog_wait_error(self, field, val):
233233
def dialog_wait_not_present(self, field):
234234
self.browser.wait_not_present(self.dialog_field(field))
235235

236-
def dialog_wait_apply_enabled(self):
236+
def dialog_wait_apply_enabled(self) -> None:
237237
self.browser.wait_attr('#dialog button.apply:nth-of-type(1)', "disabled", None)
238238

239-
def dialog_wait_apply_disabled(self):
239+
def dialog_wait_apply_disabled(self) -> None:
240240
self.browser.wait_visible('#dialog button.apply:nth-of-type(1)[disabled]')
241241

242-
def dialog_apply(self):
242+
def dialog_apply(self) -> None:
243243
self.browser.click('#dialog button.apply:nth-of-type(1)')
244244

245-
def dialog_apply_secondary(self):
245+
def dialog_apply_secondary(self) -> None:
246246
self.browser.click('#dialog button.apply:nth-of-type(2)')
247247

248-
def dialog_cancel(self):
248+
def dialog_cancel(self) -> None:
249249
self.browser.click('#dialog button.cancel')
250250

251-
def dialog_wait_close(self):
251+
def dialog_wait_close(self) -> None:
252252
# file system operations often take longer than 10s
253253
with self.browser.wait_timeout(max(self.browser.timeout, 60)):
254254
self.browser.wait_not_present('#dialog')
@@ -386,7 +386,7 @@ def udisks_objects(self):
386386
"org.freedesktop.DBus.ObjectManager",
387387
"GetManagedObjects", "", [])))""")]))
388388

389-
def configuration_field(self, dev, tab, field):
389+
def configuration_field(self, dev: str, tab: str, field: str) -> str:
390390
managerObjects = self.udisks_objects()
391391
for path in managerObjects:
392392
if "org.freedesktop.UDisks2.Block" in managerObjects[path]:
@@ -405,7 +405,7 @@ def assert_in_configuration(self, dev, tab, field, text):
405405
def assert_not_in_configuration(self, dev, tab, field, text):
406406
self.assertNotIn(text, self.configuration_field(dev, tab, field))
407407

408-
def child_configuration_field(self, dev, tab, field):
408+
def child_configuration_field(self, dev: str, tab: str, field: str) -> str:
409409
udisks_objects = self.udisks_objects()
410410
for path in udisks_objects:
411411
if "org.freedesktop.UDisks2.Encrypted" in udisks_objects[path]:
@@ -579,13 +579,13 @@ def encrypt_root(self, passphrase):
579579

580580
# Cards and tables
581581

582-
def card(self, title):
582+
def card(self, title: str) -> str:
583583
return f"[data-test-card-title='{title}']"
584584

585-
def card_parent_link(self):
585+
def card_parent_link(self) -> str:
586586
return ".pf-v5-c-breadcrumb__item:nth-last-child(2) > a"
587587

588-
def card_header(self, title):
588+
def card_header(self, title: str) -> str:
589589
return self.card(title) + " .pf-v5-c-card__header"
590590

591591
def card_row(self, title, index=None, name=None, location=None):
@@ -604,51 +604,51 @@ def click_card_row(self, title, index=None, name=None, location=None):
604604
def card_row_col(self, title, row_index=None, col_index=None, row_name=None, row_location=None):
605605
return self.card_row(title, row_index, row_name, row_location) + f" td:nth-child({col_index})"
606606

607-
def card_desc(self, card_title, desc_title):
607+
def card_desc(self, card_title: str, desc_title: str) -> str:
608608
return self.card(card_title) + f" [data-test-desc-title='{desc_title}'] [data-test-value=true]"
609609

610-
def card_desc_action(self, card_title, desc_title):
610+
def card_desc_action(self, card_title: str, desc_title: str) -> str:
611611
return self.card(card_title) + f" [data-test-desc-title='{desc_title}'] [data-test-action=true] button"
612612

613-
def card_button(self, card_title, button_title):
613+
def card_button(self, card_title: str, button_title: str) -> str:
614614
return self.card(card_title) + f" button:contains('{button_title}')"
615615

616-
def dropdown_toggle(self, parent):
616+
def dropdown_toggle(self, parent: str) -> str:
617617
return parent + " .pf-v5-c-menu-toggle"
618618

619-
def dropdown_action(self, parent, title):
619+
def dropdown_action(self, parent: str, title: str) -> str:
620620
return parent + f" .pf-v5-c-menu button:contains('{title}')"
621621

622-
def dropdown_description(self, parent, title):
622+
def dropdown_description(self, parent: str, title: str) -> str:
623623
return parent + f" .pf-v5-c-menu button:contains('{title}') .pf-v5-c-menu__item-description"
624624

625-
def click_dropdown(self, parent, title):
625+
def click_dropdown(self, parent: str, title: str) -> None:
626626
self.browser.click(self.dropdown_toggle(parent))
627627
self.browser.click(self.dropdown_action(parent, title))
628628

629-
def click_card_dropdown(self, card_title, button_title):
629+
def click_card_dropdown(self, card_title: str, button_title: str) -> None:
630630
self.click_dropdown(self.card_header(card_title), button_title)
631631

632-
def click_devices_dropdown(self, title):
632+
def click_devices_dropdown(self, title: str) -> None:
633633
self.click_card_dropdown("Storage", title)
634634

635-
def check_dropdown_action_disabled(self, parent, title, expected_text):
635+
def check_dropdown_action_disabled(self, parent: str, title: str, expected_text: str) -> None:
636636
self.browser.click(self.dropdown_toggle(parent))
637637
self.browser.wait_visible(self.dropdown_action(parent, title) + "[disabled]")
638638
self.browser.wait_text(self.dropdown_description(parent, title), expected_text)
639639
self.browser.click(self.dropdown_toggle(parent))
640640

641-
def wait_mounted(self, card_title):
641+
def wait_mounted(self, card_title: str) -> None:
642642
with self.browser.wait_timeout(30):
643643
self.browser.wait_not_in_text(self.card_desc(card_title, "Mount point"),
644644
"The filesystem is not mounted.")
645645

646-
def wait_not_mounted(self, card_title):
646+
def wait_not_mounted(self, card_title: str) -> None:
647647
with self.browser.wait_timeout(30):
648648
self.browser.wait_in_text(self.card_desc(card_title, "Mount point"),
649649
"The filesystem is not mounted.")
650650

651-
def wait_card_button_disabled(self, card_title, button_title):
651+
def wait_card_button_disabled(self, card_title: str, button_title: str) -> None:
652652
with self.browser.wait_timeout(30):
653653
self.browser.wait_visible(self.card_button(card_title, button_title) + ":disabled")
654654

test/common/testlib.py

+11-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@
4040
import unittest
4141
from collections.abc import Collection, Container, Coroutine, Iterator, Mapping, Sequence
4242
from pathlib import Path
43-
from typing import Any, Callable, ClassVar, Literal, TypedDict, TypeVar
43+
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Literal, Protocol, TypedDict, TypeVar
4444

4545
import webdriver_bidi
4646
from lcov import write_lcov
@@ -2896,3 +2896,13 @@ def sit(machines: Mapping[str, testvm.Machine] = {}) -> None:
28962896
sys.stderr.write(machine.diagnose())
28972897
print("Press RET to continue...")
28982898
sys.stdin.readline()
2899+
2900+
2901+
if TYPE_CHECKING:
2902+
class MachineProtocol(Protocol):
2903+
machine: testvm.Machine
2904+
browser: Browser
2905+
2906+
def allow_browser_errors(self, *patterns: str) -> None: ...
2907+
else:
2908+
class MachineProtocol: ...

0 commit comments

Comments
 (0)