tests: replace repeated xfail/suppresion by decorators
What changed, and why it matters
This commit is a test-code cleanup only. It replaces repeated snippets in automated tests with reusable decorators and marker aliases. No firmware behavior, cryptography, or user-facing functionality is changed, so it has no security impact on Trezor devices.
No security action required; this is a routine refactoring of test infrastructure.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The change refactors pytest test code: (1) introduces a registered marker xfail_if_no_optiga and centralizes the Optiga-detection logic in conftest.py, removing duplicated optiga_unavailable() checks from tests/device_tests/evolu/test_sign_registration.py; (2) creates a module-level alias ignore_ephemeral_keypair_warning for a warning-filter marker in tests/device_tests/thp/test_pairing.py. The logic and warning suppression remain identical; only duplication is reduced.
Changed components
tests/conftest.pytests/device_tests/evolu/test_sign_registration.pytests/device_tests/thp/test_pairing.pytests/REGISTERED_MARKERSInspect captured patch +22 / −34
diff --git a/tests/REGISTERED_MARKERS b/tests/REGISTERED_MARKERS
index 841a6d03..39b46852 100644
--- a/tests/REGISTERED_MARKERS
+++ b/tests/REGISTERED_MARKERS
@@ -18,4 +18,5 @@ solana
stellar
tezos
tron
+xfail_if_no_optiga
zcash
diff --git a/tests/conftest.py b/tests/conftest.py
index 04696c97..e8b75db4 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -397,6 +397,11 @@ def _prepared_test_ctx(
if request.node.get_closest_marker("altcoin") and is_btc_only:
pytest.skip("Skipping altcoin test")
+ # Optiga's presence is detected from the presence of its security counter.
+ has_optiga = _raw_test_ctx.features.optiga_sec is not None
+ if request.node.get_closest_marker("xfail_if_no_optiga") and not has_optiga:
+ pytest.xfail("Optiga is not available on this device.")
+
_check_protocol(request, _raw_test_ctx)
sd_marker = request.node.get_closest_marker("sd_card")
diff --git a/tests/device_tests/evolu/test_sign_registration.py b/tests/device_tests/evolu/test_sign_registration.py
index 2471d334..1ca78134 100644
--- a/tests/device_tests/evolu/test_sign_registration.py
+++ b/tests/device_tests/evolu/test_sign_registration.py
@@ -27,11 +27,6 @@ def signing_buffer(
return b"".join((compact_size(len(comp)) + comp) for comp in components)
-def optiga_unavailable(client: Client) -> bool:
- """Check if Optiga is unavailable from the presence of its security counter."""
- return client.features.optiga_sec is None
-
-
@pytest.mark.models("t2t1")
def test_evolu_sign_request_t2t1(client: Client):
challenge = bytes.fromhex("1234")
@@ -53,9 +48,8 @@ def test_evolu_sign_request_t2t1(client: Client):
@pytest.mark.models("safe")
+@pytest.mark.xfail_if_no_optiga
def test_evolu_sign_request(client: Client):
- if optiga_unavailable(client):
- pytest.xfail("Optiga is not available on this device.")
delegated_identity_key = get_delegated_identity_key(client).private_key
challenge = bytes.fromhex("1234")
size = 10
@@ -81,9 +75,8 @@ def test_evolu_sign_request(client: Client):
@pytest.mark.models("safe")
+@pytest.mark.xfail_if_no_optiga
def test_evolu_sign_request_invalid_proof(client: Client):
- if optiga_unavailable(client):
- pytest.xfail("Optiga is not available on this device.")
challenge = bytes.fromhex("1234")
size = 10
invalid_proof = get_invalid_proof(
@@ -103,9 +96,8 @@ def test_evolu_sign_request_invalid_proof(client: Client):
@pytest.mark.models("safe")
+@pytest.mark.xfail_if_no_optiga
def test_evolu_sign_request_challenge_too_long(client: Client):
- if optiga_unavailable(client):
- pytest.xfail("Optiga is not available on this device.")
challenge = b"\x01" * 300 # 300 bytes, max is 255
size = 10
proof = get_proof(
@@ -125,9 +117,8 @@ def test_evolu_sign_request_challenge_too_long(client: Client):
@pytest.mark.models("safe")
+@pytest.mark.xfail_if_no_optiga
def test_evolu_sign_request_challenge_too_short(client: Client):
- if optiga_unavailable(client):
- pytest.xfail("Optiga is not available on this device.")
challenge = b"" # 0 bytes, minimum is 1
size = 10
proof = get_proof(
@@ -147,9 +138,8 @@ def test_evolu_sign_request_challenge_too_short(client: Client):
@pytest.mark.models("safe")
+@pytest.mark.xfail_if_no_optiga
def test_evolu_sign_request_size_too_small(client: Client):
- if optiga_unavailable(client):
- pytest.xfail("Optiga is not available on this device.")
challenge = bytes.fromhex("1234")
size = -10
proof = get_proof(
@@ -171,9 +161,8 @@ def test_evolu_sign_request_size_too_small(client: Client):
@pytest.mark.models("safe")
+@pytest.mark.xfail_if_no_optiga
def test_evolu_sign_request_size_too_large(client: Client):
- if optiga_unavailable(client):
- pytest.xfail("Optiga is not available on this device.")
challenge = bytes.fromhex("1234")
size = 0xFFFFFFFF + 1
proof = get_proof(
@@ -193,9 +182,8 @@ def test_evolu_sign_request_size_too_large(client: Client):
@pytest.mark.models("safe")
+@pytest.mark.xfail_if_no_optiga
def test_evolu_sign_request_data_higher_bound(client: Client):
- if optiga_unavailable(client):
- pytest.xfail("Optiga is not available on this device.")
delegated_identity_key = get_delegated_identity_key(client).private_key
challenge = b"\x12" * 255
size = 0xFFFFFFFF
@@ -222,12 +210,10 @@ def test_evolu_sign_request_data_higher_bound(client: Client):
@pytest.mark.models("safe")
@pytest.mark.parametrize("rotation_index", [None, 0, 1, 2, 42])
+@pytest.mark.xfail_if_no_optiga
def test_evolu_sign_request_with_different_rotation_indices(
client: Client, rotation_index
):
- if optiga_unavailable(client):
- pytest.xfail("Optiga is not available on this device.")
-
evolu.index_management(client.get_session(), rotation_index=rotation_index)
delegated_identity_key = get_delegated_identity_key(client).private_key
challenge = bytes.fromhex("1234")
diff --git a/tests/device_tests/thp/test_pairing.py b/tests/device_tests/thp/test_pairing.py
index b25210b0..cdac1ad8 100644
--- a/tests/device_tests/thp/test_pairing.py
+++ b/tests/device_tests/thp/test_pairing.py
@@ -37,6 +37,10 @@ MT = t.TypeVar("MT", bound=protobuf.MessageType)
pytestmark = [pytest.mark.protocol("thp")]
+ignore_ephemeral_keypair_warning = pytest.mark.filterwarnings(
+ "ignore:One of ephemeral keypairs is already set. This is OK for testing, but should NEVER happen in production!"
+)
+
@contextmanager
def deterministic_secrets() -> t.Generator[None, None, None]:
@@ -73,9 +77,7 @@ def test_pairing_qr_code(test_ctx: TrezorTestContext) -> None:
pairing.finish()
-@pytest.mark.filterwarnings(
- "ignore:One of ephemeral keypairs is already set. This is OK for testing, but should NEVER happen in production!"
-)
+@ignore_ephemeral_keypair_warning
@deterministic_secrets()
def test_pairing_code_entry(test_ctx: TrezorTestContext) -> None:
pairing = prepare_channel_for_pairing(test_ctx, fixed_entropy=True)
@@ -92,9 +94,7 @@ def test_pairing_code_entry(test_ctx: TrezorTestContext) -> None:
pairing.finish()
-@pytest.mark.filterwarnings(
- "ignore:One of ephemeral keypairs is already set. This is OK for testing, but should NEVER happen in production!"
-)
+@ignore_ephemeral_keypair_warning
@deterministic_secrets()
def test_pairing_code_entry_invalid_cpace_key(test_ctx: TrezorTestContext) -> None:
pairing = prepare_channel_for_pairing(test_ctx, fixed_entropy=True)
@@ -117,9 +117,7 @@ def test_pairing_code_entry_invalid_cpace_key(test_ctx: TrezorTestContext) -> No
method.send_code(code_str)
-@pytest.mark.filterwarnings(
- "ignore:One of ephemeral keypairs is already set. This is OK for testing, but should NEVER happen in production!"
-)
+@ignore_ephemeral_keypair_warning
@deterministic_secrets()
def test_pairing_code_entry_invalid_cpace_key_length(
test_ctx: TrezorTestContext,
@@ -145,9 +143,7 @@ def test_pairing_code_entry_invalid_cpace_key_length(
method.send_code(code_str)
-@pytest.mark.filterwarnings(
- "ignore:One of ephemeral keypairs is already set. This is OK for testing, but should NEVER happen in production!"
-)
+@ignore_ephemeral_keypair_warning
@deterministic_secrets()
def test_pairing_code_entry_cancel(test_ctx: TrezorTestContext) -> None:
pairing = prepare_channel_for_pairing(test_ctx, fixed_entropy=True)
Why this scored 15/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.