What changed, and why it matters
This commit only adds automated user-interface tests for the battery indicator on one Trezor device model. It does not change any firmware code that users run, so it cannot introduce a security vulnerability or fix one.
No security action needed; this is a test-only change.
Security signals we found
No strong security signals were identified.
Evidence from the diff
The diff creates tests/device_tests/test_fuel_gauge.py and adds corresponding UI test fixture hashes for multiple languages in tests/ui_tests/fixtures.json. The new test exercises the debuglink set_battery_state API against the emulator for the T3W1 model and verifies the homescreen layout reports the expected state-of-charge value. No production firmware, bootloader, crypto, or host communication code is modified.
Changed components
tests/device_tests/test_fuel_gauge.pytests/ui_tests/fixtures.jsonInspect captured patch +68 / −0
diff --git a/tests/device_tests/test_fuel_gauge.py b/tests/device_tests/test_fuel_gauge.py
new file mode 100644
index 00000000..51bf5e64
--- /dev/null
+++ b/tests/device_tests/test_fuel_gauge.py
@@ -0,0 +1,62 @@
+# This file is part of the Trezor project.
+#
+# Copyright (C) 2012-2025 SatoshiLabs and contributors
+#
+# This library is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Lesser General Public License version 3
+# as published by the Free Software Foundation.
+#
+# 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 License along with this library.
+# If not, see <https://www.gnu.org/licenses/lgpl-3.0.html>.
+
+"""UI tests for the fuel gauge (battery indicator) on the Eckhart homescreen.
+
+Each test sets an emulated battery state and captures the homescreen appearance
+via the screen-recording infrastructure (--ui=record / --ui=test).
+"""
+
+import pytest
+
+from trezorlib.debuglink import DebugSession as Session
+
+# T3W1 (TS7) only - the only model with a fuel gauge on homescreen
+pytestmark = [pytest.mark.models("t3w1"), pytest.mark.emulator]
+
+# SOC boundary values that map to distinct battery icons in the UI:
+# < 10 % - EMPTY (red)
+# 10-19 % - LOW (yellow)
+# 20-39 % - MID_MINUS (grey)
+# 40-89 % - MID_PLUS (grey)
+# ≥ 90 % - FULL (grey)
+SOC_LEVELS = [5, 15, 25, 50, 95]
+
+
+@pytest.mark.setup_client(pin=None)
+def test_fuel_gauge(session: Session) -> None:
+ """Homescreen shows the correct battery icon for each SOC threshold and charging state."""
+ layout = session.debug.read_layout()
+ assert layout.main_component() == "Homescreen"
+
+ try:
+ # start at default state (usb disconnected = discharging)
+ session.debug.set_battery_state(soc=100, usb_connected=False)
+
+ for soc in SOC_LEVELS:
+ session.debug.set_battery_state(soc=soc)
+ layout = session.debug.read_layout()
+ assert layout.main_component() == "Homescreen"
+ assert layout.find_unique_value_by_key("soc", -1, int) == soc
+
+ for usb_connected in [True, False]:
+ session.debug.set_battery_state(soc=50, usb_connected=usb_connected)
+ layout = session.debug.read_layout()
+ assert layout.main_component() == "Homescreen"
+ assert layout.find_unique_value_by_key("soc", -1, int) == 50
+ finally:
+ # restore default state
+ session.debug.set_battery_state(soc=100, usb_connected=False)
diff --git a/tests/ui_tests/fixtures.json b/tests/ui_tests/fixtures.json
index 102e3134..2ce32904 100644
--- a/tests/ui_tests/fixtures.json
+++ b/tests/ui_tests/fixtures.json
@@ -34497,6 +34497,7 @@
"T3W1_cs_test_debuglink.py::test_softlock_instability": "68e87e2efd44f6d9bc143cc12d84c05547b8eb685fe7e8194da8b32b174b44cd",
"T3W1_cs_test_firmware_hash.py::test_firmware_hash_emu": "880f4d6dd67689f5a862228ef3af7b44c74f8fa9b98eb6a42987056ab1be61bd",
"T3W1_cs_test_firmware_hash.py::test_firmware_hash_hw": "25675032fe6b1628161f0cd2398edae2feace6873ae31c44ce545c91f7ced796",
+"T3W1_cs_test_fuel_gauge.py::test_fuel_gauge": "d08117922f112cc5f5cad54badaceaaffb81f8b6964a1c5f8b19ef71b6e23b56",
"T3W1_cs_test_language.py::test_build_version_mismatch": "0c119a9318288223bcd3e2a9563b4e11f5204bd96f085c562b7e914a9083e487",
"T3W1_cs_test_language.py::test_error_invalid_data_hash": "b180bba651612ba978cc9b9e3e1ebad26ff5f4598ff9f129d12eb2e7a8ae468a",
"T3W1_cs_test_language.py::test_error_invalid_data_length": "1f1689b794b646418ba7a0d9606b3aff3b14a9e3bc47f8405b9d7a3ec5646a31",
@@ -36197,6 +36198,7 @@
"T3W1_de_test_debuglink.py::test_softlock_instability": "03d285b88fe4e64ed51e6920bd23c5b6caabe4d928970a537a8bf00c265bbe52",
"T3W1_de_test_firmware_hash.py::test_firmware_hash_emu": "d4ae18dd658549c22e5036caceed6afd440f5b7842d102b914ea61717e5e060b",
"T3W1_de_test_firmware_hash.py::test_firmware_hash_hw": "aaafdc361a1d1494db1b142427ede204b978fbde090811b1ff02e54df49414f9",
+"T3W1_de_test_fuel_gauge.py::test_fuel_gauge": "6e04ae87c7e2442bd091f961bd41bb844b92edbefe79b5994b4922b7527efb49",
"T3W1_de_test_language.py::test_build_version_mismatch": "0df4724e2c82075fe90db429a6a555c74dcc233c6ca1a2f119fcd97806730310",
"T3W1_de_test_language.py::test_error_invalid_data_hash": "cf5b2d5b241fce14f103e642f35d688bff5edcaf7595b69750aca44e62cd2ed6",
"T3W1_de_test_language.py::test_error_invalid_data_length": "34ef3a2c6a24c79956fe0bde7e7539bc385253c9640a76a9caa55103704d24e7",
@@ -37897,6 +37899,7 @@
"T3W1_en_test_debuglink.py::test_softlock_instability": "ecec4ce08da800bd1f159d0f1e22f1620f41bb8e763daa6086603cfc74809f04",
"T3W1_en_test_firmware_hash.py::test_firmware_hash_emu": "97c8fc6b7b88cac50c02789eaae9f3ebf5924a6c31b0b3407a4ab962117e4799",
"T3W1_en_test_firmware_hash.py::test_firmware_hash_hw": "778dbcfb96e575c652b06902ef2842f6cc261dd3d457cc66b8b6c9338e82e1fb",
+"T3W1_en_test_fuel_gauge.py::test_fuel_gauge": "ca2b1566839dc5f70cd1b47a93c88f581b5df1d623506cf7a67da3d3a28ad294",
"T3W1_en_test_language.py::test_build_version_mismatch": "8fa59c3f73f4279a93e1d78e1122327216ead685d5bbd07a59b5fb620c38bc76",
"T3W1_en_test_language.py::test_error_invalid_data_hash": "64e8641e7d7d349594fb3d5965f0423fb70ea2daca45e3e6a2815493f4083454",
"T3W1_en_test_language.py::test_error_invalid_data_length": "2032936ef7a54bfa3a37f15db9b989da19f47efa15c333f4b7e08411b6652c00",
@@ -39597,6 +39600,7 @@
"T3W1_es_test_debuglink.py::test_softlock_instability": "50c58dc35e6f2b303a206d82831fd9b2159112b93f3660737fdacd503528b2dc",
"T3W1_es_test_firmware_hash.py::test_firmware_hash_emu": "61360ed84d53fbe7c799c23f29f859678e41a0c78a5f8163943de9c69cd01d8a",
"T3W1_es_test_firmware_hash.py::test_firmware_hash_hw": "ea59c9d24216e9bad1b1fdd99068e34dbc5beeea54b06c34219d37720f17a470",
+"T3W1_es_test_fuel_gauge.py::test_fuel_gauge": "f01bed09c9910f1dedd2dec8f7c75007b8f8c3dcb3a5b0744fe1a73b416ad966",
"T3W1_es_test_language.py::test_build_version_mismatch": "4b85e6067585cf325c0db34cbb9514fd429d83e58e67af169faf01198f7587a2",
"T3W1_es_test_language.py::test_error_invalid_data_hash": "f676d8be53736dea68ee17a838f9832f04d16bb8d1afb5141a111721b1a73b20",
"T3W1_es_test_language.py::test_error_invalid_data_length": "6149043127acebb4ecc4fdd659620f998c07afe7e3e7314f2fa7a16b975a85e4",
@@ -41297,6 +41301,7 @@
"T3W1_fr_test_debuglink.py::test_softlock_instability": "14b0f310a74cb8ccdb08e82c9508a504ce5b14bbd3125e70f72dca2f53f88997",
"T3W1_fr_test_firmware_hash.py::test_firmware_hash_emu": "2a73de87ef53e5db02c007294d32d15ccef96ae1899c4acf78286faa2be8c560",
"T3W1_fr_test_firmware_hash.py::test_firmware_hash_hw": "1cceb37657549d7798a606fb01cffd95cdd3ab59cda2e25228d11f1bb906cd31",
+"T3W1_fr_test_fuel_gauge.py::test_fuel_gauge": "4bda555498bb1cfb80c748f41bd403cb20fe73d20e59a6de16267a5fafbd61e8",
"T3W1_fr_test_language.py::test_build_version_mismatch": "6296fe39f48894537c838d02bee6157112b9e61ba62adddafc70608a34905db5",
"T3W1_fr_test_language.py::test_error_invalid_data_hash": "cd982081e30474ece7e87b7156fd14016c493d58592d91aca1e7938b8d6beedf",
"T3W1_fr_test_language.py::test_error_invalid_data_length": "fb2185494460d1a615ff6452f30d984699ce1e58b8a28b8720e6b516ab50e93e",
@@ -43002,6 +43007,7 @@
"T3W1_pt_test_debuglink.py::test_softlock_instability": "8b7882fce1a78cd1913dcd9cf70847b190e1fec8ab764a411ec6c462728cd40e",
"T3W1_pt_test_firmware_hash.py::test_firmware_hash_emu": "0bcbdde2b13d5cb61f676d80f1048e17833d95c43261f1f9f72cc895deb65eb2",
"T3W1_pt_test_firmware_hash.py::test_firmware_hash_hw": "67695a9de4ea2860aa20568a42ba0dd34e1fdf983ff4cf32347e6dd81e79c6e3",
+"T3W1_pt_test_fuel_gauge.py::test_fuel_gauge": "5715a844dc011a8be4de2233a9874aa76f80cca39845762c3ee23ee0ffbdaf43",
"T3W1_pt_test_language.py::test_build_version_mismatch": "95bc752ad519824b8996d559d1296021fa6d1d71e8dd9a4503219e24d05c1361",
"T3W1_pt_test_language.py::test_error_invalid_data_hash": "1585409d60fc177dff0ce0197bd27dd65583fa14dd1c8144e6fb4f277839354f",
"T3W1_pt_test_language.py::test_error_invalid_data_length": "cf2db63cec09019728f122060fd0992f523b06921055af7fa6c9102aa8f14df2",
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.